/* * Copyright (C) 2008-2021, 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.www.api; import com.juick.model.Message; import com.juick.model.Tag; import com.juick.model.User; import com.juick.util.WebUtils; import com.juick.www.WebApp; import com.juick.model.CommandResult; import com.juick.util.HttpBadRequestException; import com.juick.util.HttpForbiddenException; import com.juick.util.HttpNotFoundException; import com.juick.service.MessagesService; import com.juick.service.TagService; import com.juick.service.UserService; import com.juick.service.component.SystemEvent; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.io.Resource; import org.springframework.http.MediaType; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import javax.inject.Inject; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; /** * @author ugnich */ @RestController @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE) public class Messages { @Inject private MessagesService messagesService; @Inject private UserService userService; @Inject private TagService tagService; @Inject private ApplicationEventPublisher applicationEventPublisher; @Inject private WebApp webApp; @Value("classpath:Transparent.gif") private Resource invisiblePixel; @Inject private User serviceUser; // TODO: serialize image urls @GetMapping({"/api/home"}) public List getHome(@ModelAttribute User visitor, @RequestParam(defaultValue = "0") int before_mid) { int vuid = visitor.getUid(); List mids = messagesService.getMyFeed(vuid, before_mid, true); List msgs = messagesService.getMessages(visitor, mids); msgs.forEach(m -> m.getUser().setAvatar(webApp.getAvatarUrl(m.getUser()))); return msgs; } @GetMapping("/api/messages") public List getMessages(@ModelAttribute User visitor, @RequestParam(required = false) String uname, @RequestParam(name = "before_mid", defaultValue = "0") Integer before, @RequestParam(required = false, defaultValue = "0") Integer daysback, @RequestParam(required = false) String withrecommended, @RequestParam(required = false) String popular, @RequestParam(required = false) String search, @RequestParam(required = false, defaultValue = "0") Integer page, @RequestParam(required = false) String media, @RequestParam(required = false) String tag) { List mids; if (StringUtils.hasText(uname)) { User user = userService.getUserByName(uname); if (!user.isAnonymous() && !user.isBanned()) { if (StringUtils.hasText(media)) { mids = messagesService.getUserPhotos(user.getUid(), 0, before); } else if (StringUtils.hasText(tag)) { Tag tagObject = tagService.getTag(tag, false); if (tagObject != null) { mids = messagesService.getUserTag(user.getUid(), tagObject.TID, 0, before); } else { throw new HttpNotFoundException(); } } else if (StringUtils.hasText(withrecommended)) { mids = messagesService.getUserBlogWithRecommendations(user, visitor, 0, before); } else if (daysback > 0) { mids = messagesService.getUserBlogAtDay(user.getUid(), 0, daysback); } else if (StringUtils.hasText(search)) { mids = messagesService.getUserSearch(visitor, user.getUid(), WebUtils.encodeSphinx(search), 0, page); } else { mids = messagesService.getUserBlog(user.getUid(), 0, before); } } else { throw new HttpNotFoundException(); } } else { if (StringUtils.hasText(popular)) { mids = messagesService.getUserBlogWithRecommendations(serviceUser, visitor, 0, before); } else if (StringUtils.hasText(media)) { mids = messagesService.getPhotos(visitor.getUid(), before); } else if (StringUtils.hasText(tag)) { Tag tagObject = tagService.getTag(tag, false); if (tagObject != null) { mids = messagesService.getTag(tagObject.TID, visitor.getUid(), before, 20); } else { throw new HttpNotFoundException(); } } else if (StringUtils.hasText(search)) { mids = messagesService.getSearch(visitor, WebUtils.encodeSphinx(search), page); } else { mids = messagesService.getAll(visitor.getUid(), before); } } List msgs = messagesService.getMessages(visitor, mids); msgs.forEach(m -> m.getUser().setAvatar(webApp.getAvatarUrl(m.getUser()))); return msgs; } @DeleteMapping("/api/messages") public CommandResult deleteMessage(@ModelAttribute User visitor, @RequestParam int mid, @RequestParam(required = false, defaultValue = "0") int rid) { if (rid > 0) { if (messagesService.deleteReply(visitor.getUid(), mid, rid)) { return CommandResult.fromString("Reply deleted"); } } if (messagesService.deleteMessage(visitor.getUid(), mid)) { return CommandResult.fromString("Message deleted"); } throw new HttpBadRequestException(); } @GetMapping("/api/messages/discussions") public List getDiscussions(@ModelAttribute User visitor, @RequestParam(required = false, defaultValue = "0") Long to) { List msgs = messagesService.getMessages(visitor, messagesService.getDiscussions(visitor.getUid(), to)); msgs.forEach(m -> m.getUser().setAvatar(webApp.getAvatarUrl(m.getUser()))); return msgs; } @GetMapping("/api/thread") public List getThread(@ModelAttribute User visitor, @RequestParam(defaultValue = "0") int mid, @RequestParam(defaultValue = "true") boolean showReplies) { Optional message = messagesService.getMessage(mid); if (message.isPresent()) { Message msg = message.get(); if (!messagesService.canViewThread(mid, visitor.getUid())) { throw new HttpForbiddenException(); } else { msg.getUser().setAvatar(webApp.getAvatarUrl(msg.getUser())); msg.getRecommendations().forEach(r -> r.setAvatar(webApp.getAvatarUrl(r))); if (showReplies) { List replies = messagesService.getReplies(visitor, mid); // update replies counter to exclude banned replies msg.setReplies(replies.size()); if (!visitor.isAnonymous()) { userService.updateLastSeen(visitor); applicationEventPublisher .publishEvent(new SystemEvent(this, SystemActivity.read(visitor, msg))); } replies.add(0, msg); return replies; } else { return List.of(msg); } } } throw new HttpNotFoundException(); } @GetMapping(value = "/api/thread/mark_read/{mid}-{rid}.gif", produces = MediaType.IMAGE_GIF_VALUE) public byte[] markThreadRead(@ModelAttribute User visitor, @PathVariable int mid, @PathVariable int rid) throws IOException { if (!visitor.isAnonymous()) { messagesService.setLastReadComment(visitor, mid, rid); Message msg = messagesService.getMessage(mid).orElseThrow(HttpNotFoundException::new); userService.updateLastSeen(visitor); applicationEventPublisher.publishEvent(new SystemEvent(this, SystemActivity.read(visitor, msg))); return IOUtils.toByteArray(invisiblePixel.getInputStream()); } throw new HttpBadRequestException(); } }