From a608baeed738894433aacfa041e2617f60ce959f Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Sat, 4 Apr 2020 01:15:01 +0300 Subject: Initialize all components from configuration --- .../com/juick/util/HeaderRequestInterceptor.java | 43 +++++ .../com/juick/util/HttpBadRequestException.java | 31 ++++ .../com/juick/util/HttpForbiddenException.java | 33 ++++ .../java/com/juick/util/HttpNotFoundException.java | 32 ++++ src/main/java/com/juick/util/HttpUtils.java | 115 ++++++++++++++ src/main/java/com/juick/util/ImageUtils.java | 173 +++++++++++++++++++++ src/main/java/com/juick/util/TagUtils.java | 42 +++++ src/main/java/com/juick/util/WebUtils.java | 79 ++++++++++ .../com/juick/util/adapters/SimpleDateAdapter.java | 40 +++++ .../com/juick/util/annotation/UserCommand.java | 50 ++++++ .../juick/util/formatters/PlainTextFormatter.java | 105 +++++++++++++ .../java/com/juick/util/xmpp/JidConverter.java | 32 ++++ .../java/com/juick/util/xmpp/iq/MessageQuery.java | 27 ++++ .../java/com/juick/util/xmpp/iq/package-info.java | 25 +++ 14 files changed, 827 insertions(+) create mode 100644 src/main/java/com/juick/util/HeaderRequestInterceptor.java create mode 100644 src/main/java/com/juick/util/HttpBadRequestException.java create mode 100644 src/main/java/com/juick/util/HttpForbiddenException.java create mode 100644 src/main/java/com/juick/util/HttpNotFoundException.java create mode 100644 src/main/java/com/juick/util/HttpUtils.java create mode 100644 src/main/java/com/juick/util/ImageUtils.java create mode 100644 src/main/java/com/juick/util/TagUtils.java create mode 100644 src/main/java/com/juick/util/WebUtils.java create mode 100644 src/main/java/com/juick/util/adapters/SimpleDateAdapter.java create mode 100644 src/main/java/com/juick/util/annotation/UserCommand.java create mode 100644 src/main/java/com/juick/util/formatters/PlainTextFormatter.java create mode 100644 src/main/java/com/juick/util/xmpp/JidConverter.java create mode 100644 src/main/java/com/juick/util/xmpp/iq/MessageQuery.java create mode 100644 src/main/java/com/juick/util/xmpp/iq/package-info.java (limited to 'src/main/java/com/juick/util') diff --git a/src/main/java/com/juick/util/HeaderRequestInterceptor.java b/src/main/java/com/juick/util/HeaderRequestInterceptor.java new file mode 100644 index 00000000..22a3de06 --- /dev/null +++ b/src/main/java/com/juick/util/HeaderRequestInterceptor.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-2020, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util; + +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; + +import java.io.IOException; + +public class HeaderRequestInterceptor implements ClientHttpRequestInterceptor { + + private final String headerName; + + private final String headerValue; + + public HeaderRequestInterceptor(String headerName, String headerValue) { + this.headerName = headerName; + this.headerValue = headerValue; + } + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + request.getHeaders().set(headerName, headerValue); + return execution.execute(request, body); + } +} diff --git a/src/main/java/com/juick/util/HttpBadRequestException.java b/src/main/java/com/juick/util/HttpBadRequestException.java new file mode 100644 index 00000000..386b52df --- /dev/null +++ b/src/main/java/com/juick/util/HttpBadRequestException.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008-2020, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Created by vt on 24/11/2016. + */ +@ResponseStatus(value = HttpStatus.BAD_REQUEST) +public class HttpBadRequestException extends RuntimeException { + public HttpBadRequestException() { + super("the request was bad", null, false, false); + } +} diff --git a/src/main/java/com/juick/util/HttpForbiddenException.java b/src/main/java/com/juick/util/HttpForbiddenException.java new file mode 100644 index 00000000..e2211574 --- /dev/null +++ b/src/main/java/com/juick/util/HttpForbiddenException.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2008-2020, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Created by vt on 24/11/2016. + */ +@ResponseStatus(value = HttpStatus.FORBIDDEN) +public class HttpForbiddenException extends RuntimeException { + public HttpForbiddenException() { + super(StringUtils.EMPTY, null, false, false); + } + +} diff --git a/src/main/java/com/juick/util/HttpNotFoundException.java b/src/main/java/com/juick/util/HttpNotFoundException.java new file mode 100644 index 00000000..f9d1ffbe --- /dev/null +++ b/src/main/java/com/juick/util/HttpNotFoundException.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008-2020, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Created by vt on 24/11/2016. + */ +@ResponseStatus(value = HttpStatus.NOT_FOUND) +public class HttpNotFoundException extends RuntimeException { + public HttpNotFoundException() { + super(StringUtils.EMPTY, null, false, false); + } +} diff --git a/src/main/java/com/juick/util/HttpUtils.java b/src/main/java/com/juick/util/HttpUtils.java new file mode 100644 index 00000000..46bb3e2d --- /dev/null +++ b/src/main/java/com/juick/util/HttpUtils.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2008-2020, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.juick.util; + +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; +import org.springframework.web.multipart.MultipartFile; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Iterator; +import java.util.UUID; + +/** + * + * @author Ugnich Anton + */ +public class HttpUtils { + private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class); + + public static URI receiveMultiPartFile(MultipartFile attach, String tmpDir) throws IOException { + if (attach != null && !attach.isEmpty()) { + ImageInputStream iis = ImageIO.createImageInputStream(attach.getInputStream()); + Iterator readers = ImageIO.getImageReaders(iis); + + String format = StringUtils.EMPTY; + while (readers.hasNext()) { + ImageReader read = readers.next(); + format = read.getFormatName(); + } + String attachmentType = attachmentTypeFromFormat(format); + if (attachmentType.equals("jpg") || attachmentType.equals("png")) { + String attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType; + try { + Files.write(Paths.get(tmpDir, attachmentFName), + attach.getBytes()); + return URI.create(String.format("juick://%s", attachmentFName)); + } catch (IOException e) { + logger.warn("file receive error", e); + } + } + logger.warn("file type is unknown: {}", attach.getOriginalFilename()); + } + return URI.create(StringUtils.EMPTY); + } + + private static String attachmentTypeFromFormat(String format) throws IOException { + if (format != null && format.equals("JPEG")) { + return "jpg"; + } else if (format != null && format.equals("png")) { + return "png"; + } else { + throw new IOException("Wrong file type: " + format); + } + } + + public static String mediaType(String attachmentType) { + return attachmentType.equals("jpg") ? MediaType.IMAGE_JPEG_VALUE : MediaType.IMAGE_PNG_VALUE; + } + + public static URI downloadImage(URL url, String tmpDir) throws IOException { + ImageInputStream iis = ImageIO.createImageInputStream(url.openStream()); + Iterator readers = ImageIO.getImageReaders(iis); + + String format = StringUtils.EMPTY; + while (readers.hasNext()) { + ImageReader read = readers.next(); + format = read.getFormatName(); + } + URLConnection urlConn; + try { + urlConn = url.openConnection(); + } catch (IOException e) { + logger.error(String.format("Failed open url: %s", url.toString())); + throw e; + } + + try (InputStream is = new BufferedInputStream(urlConn.getInputStream())) { + String attachmentType = attachmentTypeFromFormat(format); + + String attachmentFName = DigestUtils.md5Hex(UUID.randomUUID().toString()) + "." + attachmentType; + Files.copy(is, Paths.get(tmpDir, attachmentFName)); + return URI.create(String.format("juick://%s", attachmentFName)); + } catch (IOException e) { + logger.error(String.format("Failed download image by url: %s", url.toString()), e); + throw e; + } + } +} diff --git a/src/main/java/com/juick/util/ImageUtils.java b/src/main/java/com/juick/util/ImageUtils.java new file mode 100644 index 00000000..38e1de08 --- /dev/null +++ b/src/main/java/com/juick/util/ImageUtils.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2008-2020, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util; + +import com.juick.model.Attachment; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.Imaging; +import org.apache.commons.imaging.common.ImageMetadata; +import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; +import org.apache.commons.imaging.formats.tiff.TiffField; +import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.imgscalr.Scalr; +import org.imgscalr.Scalr.Rotation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Iterator; + +public class ImageUtils { + private static final Logger logger = LoggerFactory.getLogger(ImageUtils.class); + + private String imgDir; + private String tmpDir; + + public ImageUtils(String imgDir, String tmpDir) { + this.imgDir = imgDir; + this.tmpDir = tmpDir; + } +/** + * Returns BufferedImage, same as ImageIO.read() does. + * + *

JPEG images with EXIF metadata are rotated according to Orientation tag. + * + * @param imageFile a File to read from. + */ + private static BufferedImage readImageWithOrientation(File imageFile) + throws IOException { + + BufferedImage image = ImageIO.read(imageFile); + if (!FilenameUtils.getExtension(imageFile.getName()).equals("jpg")) { + return image; + } + + try { + ImageMetadata metadata = Imaging.getMetadata(imageFile); + + if (metadata instanceof JpegImageMetadata) { + JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; + TiffField orientationField = jpegMetadata.findEXIFValue(TiffTagConstants.TIFF_TAG_ORIENTATION); + + if (orientationField != null) { + int orientation = orientationField.getIntValue(); + switch (orientation) { + case TiffTagConstants.ORIENTATION_VALUE_ROTATE_90_CW: + image = Scalr.rotate(image, Rotation.CW_90); + break; + case TiffTagConstants.ORIENTATION_VALUE_ROTATE_180: + image = Scalr.rotate(image, Rotation.CW_180); + break; + case TiffTagConstants.ORIENTATION_VALUE_ROTATE_270_CW: + image = Scalr.rotate(image, Rotation.CW_270); + break; + case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL: + image = Scalr.rotate(image, Rotation.FLIP_HORZ); + break; + case TiffTagConstants.ORIENTATION_VALUE_MIRROR_VERTICAL: + image = Scalr.rotate(image, Rotation.FLIP_VERT); + break; + case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_90_CW: + image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_90); + break; + case TiffTagConstants.ORIENTATION_VALUE_MIRROR_HORIZONTAL_AND_ROTATE_270_CW: + image = Scalr.rotate(Scalr.rotate(image, Rotation.FLIP_HORZ), Rotation.CW_270); + break; + case TiffTagConstants.ORIENTATION_VALUE_HORIZONTAL_NORMAL: + default: + // do nothing + break; + } + } + } + } catch (ImageReadException | IOException e) { + // failed to read metadata. + // nothing to do here, return image as is. + } + + return image; + } + + public void saveImageWithPreviews(String tempFilename, String outputFilename) + throws IOException { + String ext = FilenameUtils.getExtension(outputFilename); + + Path outputImagePath = Paths.get(imgDir, "p", outputFilename); + // this throws strange exceptions + // Files.move(Paths.get(tmpDir, tempFilename), outputImagePath); + FileUtils.moveFile(Paths.get(tmpDir, tempFilename).toFile(), outputImagePath.toFile()); + BufferedImage originalImage = readImageWithOrientation(outputImagePath.toFile()); + + int width = originalImage.getWidth(); + int height = originalImage.getHeight(); + int maxDimension = Math.max(width, height); + BufferedImage image1024 = (maxDimension > 1024) ? Scalr.resize(originalImage, 1024) : originalImage; + BufferedImage image0512 = (maxDimension > 512) ? Scalr.resize(originalImage, 512) : originalImage; + BufferedImage image0160 = (maxDimension > 160) ? Scalr.resize(originalImage, 160) : originalImage; + ImageIO.write(image1024, ext, Paths.get(imgDir, "photos-1024", outputFilename).toFile()); + ImageIO.write(image0512, ext, Paths.get(imgDir, "photos-512", outputFilename).toFile()); + ImageIO.write(image0160, ext, Paths.get(imgDir, "ps", outputFilename).toFile()); + } + + public void saveAvatar(String tempFilename, int uid) + throws IOException { + String ext = FilenameUtils.getExtension(tempFilename); + String originalName = String.format("%s.%s", uid, ext); + Path originalPath = Paths.get(imgDir, "ao", originalName); + Files.move(Paths.get(tmpDir, tempFilename), originalPath, StandardCopyOption.REPLACE_EXISTING); + BufferedImage originalImage = ImageIO.read(originalPath.toFile()); + + String targetExt = "png"; + String targetName = String.format("%s.%s", uid, targetExt); + ImageIO.write(Scalr.resize(originalImage, 96), targetExt, Paths.get(imgDir, "a", targetName).toFile()); + ImageIO.write(Scalr.resize(originalImage, 32), targetExt, Paths.get(imgDir, "as", targetName).toFile()); + } + public Attachment getAttachment(File imgFile) throws IOException { + Attachment attachment = new Attachment(); + try (ImageInputStream stream = ImageIO.createImageInputStream(imgFile)) { + Iterator iter = ImageIO.getImageReaders(stream); + while (iter.hasNext()) { + ImageReader reader = iter.next(); + try { + reader.setInput(stream); + attachment.setWidth(reader.getWidth(reader.getMinIndex())); + attachment.setHeight(reader.getHeight(reader.getMinIndex())); + return attachment; + } catch (Exception e) { + logger.debug("Error reading {}, trying next reader", imgFile.getAbsolutePath()); + } finally { + reader.dispose(); + } + } + } + + logger.warn("Not a known image file {}", imgFile.getAbsolutePath()); + return attachment; + } +} \ No newline at end of file diff --git a/src/main/java/com/juick/util/TagUtils.java b/src/main/java/com/juick/util/TagUtils.java new file mode 100644 index 00000000..2ec03e48 --- /dev/null +++ b/src/main/java/com/juick/util/TagUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008-2020, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util; + +import com.juick.model.Tag; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Created by aalexeev on 11/13/16. + */ +public class TagUtils { + private TagUtils() { + throw new IllegalStateException(); + } + + public static String toString(final List tags) { + if (CollectionUtils.isEmpty(tags)) + return StringUtils.EMPTY; + + return tags.stream().map(t -> "*" + t.getName()) + .collect(Collectors.joining(" ")); + } +} diff --git a/src/main/java/com/juick/util/WebUtils.java b/src/main/java/com/juick/util/WebUtils.java new file mode 100644 index 00000000..3a8c7620 --- /dev/null +++ b/src/main/java/com/juick/util/WebUtils.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008-2020, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util; + +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; +import java.util.regex.Pattern; + +/** + * Created by aalexeev on 11/28/16. + */ +public class WebUtils { + private WebUtils() { + throw new IllegalStateException(); + } + + private static final Pattern USER_NAME_PATTERN = Pattern.compile("[a-zA-Z-_\\d]{2,16}"); + + private static final Pattern POST_NUMBER_PATTERN = Pattern.compile("-?\\d+"); + + private static final Pattern JID_PATTERN = Pattern.compile("^[a-zA-Z0-9\\\\-\\\\_\\\\@\\\\.]{6,64}$"); + + + public static boolean isPostNumber(final String aString) { + return aString != null && POST_NUMBER_PATTERN.matcher(aString).matches(); + } + + public static boolean isNotPostNumber(final String aString) { + return !isPostNumber(aString); + } + + public static boolean isUserName(final String aString) { + return aString != null && USER_NAME_PATTERN.matcher(aString).matches(); + } + + public static boolean isNotUserName(final String aString) { + return !isUserName(aString); + } + + public static boolean isJid(final String aString) { + return aString != null && JID_PATTERN.matcher(aString).matches(); + } + + public static boolean isNotJid(final String aString) { + return !isJid(aString); + } + + public static String encodeSphinx(String str) { + return str.replaceAll("@", "\\\\@") + .replaceAll("\\'", "\\\\'") + .replaceAll("=", "\\\\\\\\="); + } + /** + * Returns the viewName to return for coming back to the sender url + * + * @param request Instance of {@link HttpServletRequest} or use an injected instance + * @return Optional with the view name. Recomended to use an alternativa url with + * {@link Optional#orElse(java.lang.Object)} + */ + public static Optional getPreviousPageByRequest(HttpServletRequest request) + { + return Optional.ofNullable(request.getHeader("Referer")); + } +} diff --git a/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java b/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java new file mode 100644 index 00000000..fd5f3b5a --- /dev/null +++ b/src/main/java/com/juick/util/adapters/SimpleDateAdapter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2008-2019, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util.adapters; + +import com.juick.util.DateFormattersHolder; + +import javax.xml.bind.annotation.adapters.XmlAdapter; +import java.time.Instant; + +/** + * Created by vitalyster on 15.11.2016. + */ + +public class SimpleDateAdapter extends XmlAdapter { + + @Override + public String marshal(Instant v) { + return DateFormattersHolder.getMessageFormatterInstance().format(v); + } + + @Override + public Instant unmarshal(String v) { + return DateFormattersHolder.getMessageFormatterInstance().parse(v); + } +} diff --git a/src/main/java/com/juick/util/annotation/UserCommand.java b/src/main/java/com/juick/util/annotation/UserCommand.java new file mode 100644 index 00000000..29e40ca2 --- /dev/null +++ b/src/main/java/com/juick/util/annotation/UserCommand.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008-2019, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util.annotation; + +import org.apache.commons.lang3.StringUtils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by oxpa on 22.03.16. + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface UserCommand { + /** + * + * @return a command pattern + */ + String pattern() default StringUtils.EMPTY; + + /** + * + * @return pattern flags + */ + int patternFlags() default 0; + + /** + * + * @return a string used in HELP command output. Basically, only 1 string + */ + String help() default StringUtils.EMPTY; +} diff --git a/src/main/java/com/juick/util/formatters/PlainTextFormatter.java b/src/main/java/com/juick/util/formatters/PlainTextFormatter.java new file mode 100644 index 00000000..b5d67030 --- /dev/null +++ b/src/main/java/com/juick/util/formatters/PlainTextFormatter.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2008-2019, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util.formatters; + +import com.juick.model.Message; +import com.juick.util.MessageUtils; +import org.apache.commons.lang3.StringUtils; +import org.ocpsoft.prettytime.PrettyTime; + +import java.util.Date; +import java.util.Locale; + +/** + * Created by vitalyster on 12.10.2016. + */ +public class PlainTextFormatter { + static PrettyTime pt = new PrettyTime(new Locale("en")); + + public static String formatPost(Message jmsg) { + return formatPost(jmsg, false); + } + + public static String formatPost(Message jmsg, boolean markdown) { + StringBuilder sb = new StringBuilder(); + String title = MessageUtils.isReply(jmsg) ? "Reply by @" : MessageUtils.isPM(jmsg) ? "Private message from @" : "@"; + String subtitle = MessageUtils.isReply(jmsg) ? markdown ? MessageUtils.escapeMarkdown(StringUtils.defaultString(jmsg.getReplyQuote())) + : jmsg.getReplyQuote() + : markdown ? MessageUtils.getMessageHashTags(jmsg) : MessageUtils.getTagsString(jmsg); + sb.append(title).append(markdown ? MessageUtils.getMarkdownUser(jmsg.getUser()) : jmsg.getUser().getName()).append(":\n") + .append(subtitle).append("\n"); + if (markdown) { + sb.append(MessageUtils.formatMarkdownText(jmsg)); + } else { + sb.append(StringUtils.defaultString(jmsg.getText())); + } + sb.append("\n"); + if (!markdown && StringUtils.isNotEmpty(jmsg.getAttachmentType())) { + sb.append(MessageUtils.attachmentUrl(jmsg)); + } + return sb.toString(); + } + + public static String formatPostSummary(Message m) { + int cropLength = 384; + String timeAgo = pt.format(Date.from(m.getCreated())); + String repliesCount = m.getReplies() == 1 ? "; 1 reply" : m.getReplies() == 0 ? "" + : String.format("; %d replies", m.getReplies()); + StringBuilder sb = new StringBuilder(); + String txt = StringUtils.defaultString(m.getText()); + String attachmentUrl = MessageUtils.attachmentUrl(m); + if (StringUtils.isNotEmpty(attachmentUrl)) { + sb.append(attachmentUrl).append("\n"); + } + if (txt.length() >= cropLength) { + sb.append(StringUtils.substring(txt, 0, cropLength)).append(" [...]"); + } else { + sb.append(txt); + } + return String.format("@%s:%s\n%s\n#%s (%s%s) %s", + m.getUser().getName(), MessageUtils.getTagsString(m), sb.toString(), formatPostNumber(m), timeAgo, repliesCount, formatUrl(m)); + } + + public static String formatUrl(Message jmsg) { + if (MessageUtils.isReply(jmsg)) { + return String.format("https://juick.com/m/%d#%d", jmsg.getMid(), jmsg.getRid()); + } else if (MessageUtils.isPM(jmsg)) { + return "https://juick.com/pm/inbox"; + } + return "https://juick.com/m/" + jmsg.getMid(); + } + + public static String markdownUrl(String url, String description) { + if (StringUtils.isNotBlank(description)) { + return String.format("[%s](%s)", description, url); + } else { + return url; + } + } + + public static String formatPostNumber(Message jmsg) { + if (jmsg.getRid() > 0) { + return String.format("%d/%d", jmsg.getMid(), jmsg.getRid()); + } + return String.format("%d", jmsg.getMid()); + } + + public static String formatTwitterCard(Message jmsg) { + return MessageUtils.getMessageHashTags(jmsg) + StringUtils.defaultString(jmsg.getText()); + } +} diff --git a/src/main/java/com/juick/util/xmpp/JidConverter.java b/src/main/java/com/juick/util/xmpp/JidConverter.java new file mode 100644 index 00000000..118c6711 --- /dev/null +++ b/src/main/java/com/juick/util/xmpp/JidConverter.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008-2019, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util.xmpp; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.lang.Nullable; +import rocks.xmpp.addr.Jid; + +import javax.annotation.Nonnull; + +public class JidConverter implements Converter { + @Nullable + @Override + public Jid convert(@Nonnull String jidStr) { + return Jid.of(jidStr); + } +} diff --git a/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java b/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java new file mode 100644 index 00000000..c1096e8e --- /dev/null +++ b/src/main/java/com/juick/util/xmpp/iq/MessageQuery.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2008-2019, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.util.xmpp.iq; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "query") +public class MessageQuery { + private MessageQuery() { + + } +} diff --git a/src/main/java/com/juick/util/xmpp/iq/package-info.java b/src/main/java/com/juick/util/xmpp/iq/package-info.java new file mode 100644 index 00000000..7a1694a5 --- /dev/null +++ b/src/main/java/com/juick/util/xmpp/iq/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2008-2019, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlSchema(namespace = "http://juick.com/query#messages", elementFormDefault = XmlNsForm.QUALIFIED) +package com.juick.util.xmpp.iq; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlNsForm; +import javax.xml.bind.annotation.XmlSchema; \ No newline at end of file -- cgit v1.2.3