aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/juick/www/api/activity
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/juick/www/api/activity')
-rw-r--r--src/main/java/com/juick/www/api/activity/Profile.java410
-rw-r--r--src/main/java/com/juick/www/api/activity/helpers/ActivityIdDeserializer.java38
-rw-r--r--src/main/java/com/juick/www/api/activity/helpers/LinkValueDeserializer.java38
-rw-r--r--src/main/java/com/juick/www/api/activity/model/Activity.java50
-rw-r--r--src/main/java/com/juick/www/api/activity/model/Context.java144
-rw-r--r--src/main/java/com/juick/www/api/activity/model/activities/Accept.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/activities/Announce.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/activities/Block.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/activities/Create.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/activities/Delete.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/activities/Follow.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/activities/Like.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/activities/Undo.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/activities/Update.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Emoji.java23
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Hashtag.java28
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Image.java32
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Key.java41
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Link.java32
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Mention.java29
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Note.java81
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/OrderedCollection.java42
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/OrderedCollectionPage.java75
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Person.java104
24 files changed, 1374 insertions, 0 deletions
diff --git a/src/main/java/com/juick/www/api/activity/Profile.java b/src/main/java/com/juick/www/api/activity/Profile.java
new file mode 100644
index 00000000..bdd7cab2
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/Profile.java
@@ -0,0 +1,410 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.juick.model.Message;
+import com.juick.model.User;
+import com.juick.formatters.PlainTextFormatter;
+import com.juick.model.CommandResult;
+import com.juick.server.ActivityPubManager;
+import com.juick.server.CommandsManager;
+import com.juick.server.KeystoreManager;
+import com.juick.server.SignatureManager;
+import com.juick.www.api.activity.model.Activity;
+import com.juick.www.api.activity.model.Context;
+import com.juick.www.api.activity.model.activities.Announce;
+import com.juick.www.api.activity.model.activities.Create;
+import com.juick.www.api.activity.model.activities.Delete;
+import com.juick.www.api.activity.model.activities.Follow;
+import com.juick.www.api.activity.model.activities.Like;
+import com.juick.www.api.activity.model.activities.Undo;
+import com.juick.www.api.activity.model.objects.Image;
+import com.juick.www.api.activity.model.objects.Key;
+import com.juick.www.api.activity.model.objects.Note;
+import com.juick.www.api.activity.model.objects.OrderedCollection;
+import com.juick.www.api.activity.model.objects.OrderedCollectionPage;
+import com.juick.www.api.activity.model.objects.Person;
+import com.juick.server.util.HttpNotFoundException;
+import com.juick.www.WebApp;
+import com.juick.service.MessagesService;
+import com.juick.service.UserService;
+import com.juick.service.activities.AnnounceEvent;
+import com.juick.service.activities.FollowEvent;
+import com.juick.service.activities.UndoAnnounceEvent;
+import com.juick.service.activities.UndoFollowEvent;
+import com.juick.service.security.annotation.Visitor;
+import com.overzealous.remark.Remark;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import javax.inject.Inject;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+@RestController
+public class Profile {
+ private static final Logger logger = LoggerFactory.getLogger("ActivityPub");
+ @Inject
+ private UserService userService;
+ @Inject
+ private MessagesService messagesService;
+ @Inject
+ private KeystoreManager keystoreManager;
+ @Inject
+ private SignatureManager signatureManager;
+ @Inject
+ private ActivityPubManager activityPubManager;
+ @Inject
+ private ApplicationEventPublisher applicationEventPublisher;
+ @Inject
+ private CommandsManager commandsManager;
+ @Value("${web_domain:localhost}")
+ private String domain;
+ @Value("${ap_base_uri:http://localhost:8080/}")
+ private String baseUri;
+ @Inject
+ private ObjectMapper jsonMapper;
+ @Inject
+ private WebApp webApp;
+ @Inject
+ private Remark remarkConverter;
+
+ @GetMapping(value = "/u/{userName}", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
+ public Person getUser(@PathVariable String userName) {
+ User user = userService.getUserByName(userName);
+ if (!user.isAnonymous()) {
+ Person person = new Person();
+ person.setId(activityPubManager.personUri(user));
+ person.setUrl(activityPubManager.personWebUri(user));
+ person.setName(userName);
+ person.setPreferredUsername(userName);
+ Key publicKey = new Key();
+ publicKey.setId(person.getId() + "#main-key");
+ publicKey.setOwner(person.getId());
+ publicKey.setPublicKeyPem(keystoreManager.getPublicKeyPem());
+ person.setPublicKey(publicKey);
+ person.setInbox(activityPubManager.inboxUri());
+ person.setOutbox(activityPubManager.outboxUri(user));
+ person.setFollowers(activityPubManager.followersUri(user));
+ person.setFollowing(activityPubManager.followingUri(user));
+ Image avatar = new Image();
+ avatar.setUrl(webApp.getAvatarUrl(user));
+ avatar.setMediaType("image/png");
+ person.setIcon(avatar);
+ return (Person) Context.build(person);
+ }
+ throw new HttpNotFoundException();
+ }
+
+ @GetMapping(value = "/u/{userName}/blog/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
+ public OrderedCollection getOutbox(@PathVariable String userName) {
+ User user = userService.getUserByName(userName);
+ if (!user.isAnonymous()) {
+ UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri);
+ OrderedCollection blog = new OrderedCollection();
+ blog.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString());
+ blog.setTotalItems(userService.getStatsMessages(user.getUid()));
+ blog.setFirst(uriComponentsBuilder.path(String.format("/u/%s/blog", userName)).toUriString());
+ return (OrderedCollection) Context.build(blog);
+ }
+ throw new HttpNotFoundException();
+ }
+
+ @GetMapping(value = "/u/{userName}/blog", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
+ public OrderedCollectionPage getOutboxPage(@Visitor User visitor, @PathVariable String userName,
+ @RequestParam(required = false, defaultValue = "0") int before) {
+ User user = userService.getUserByName(userName);
+ if (!user.isAnonymous() && !user.isBanned()) {
+ UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri);
+ String personUri = uri.path(String.format("/u/%s", userName)).toUriString();
+ List<Integer> mids = messagesService.getUserBlog(user.getUid(), 0, before);
+ List<Note> notes = messagesService.getMessages(visitor, mids)
+ .stream().map(activityPubManager::makeNote).collect(Collectors.toList());
+ OrderedCollectionPage page = new OrderedCollectionPage();
+ page.setPartOf(uri.replacePath(String.format("/u/%s/blog/toc", userName)).toUriString());
+ page.setFirst(uri.replacePath(String.format("/u/%s/blog", userName)).toUriString());
+ page.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString());
+ page.setOrderedItems(notes.stream().map(a -> {
+ Create create = new Create();
+ create.setId(a.getId() + "#Create");
+ create.setTo(a.getTo());
+ create.setActor(personUri);
+ create.setObject(a);
+ create.setPublished(a.getPublished());
+ return create;
+ }).collect(Collectors.toList()));
+ int beforeNext = mids.stream().reduce((fst, second) -> second).orElse(0);
+ if (beforeNext > 0) {
+ page.setNext(uri.queryParam("before", beforeNext).toUriString());
+ }
+ page.setLast(uri.replaceQueryParam("before", "1").toUriString());
+ return (OrderedCollectionPage) Context.build(page);
+ }
+ throw new HttpNotFoundException();
+ }
+
+ @GetMapping(value = "/u/{userName}/followers/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
+ public OrderedCollection getFollowers(@PathVariable String userName) {
+ User user = userService.getUserByName(userName);
+ if (!user.isAnonymous()) {
+ UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri);
+ OrderedCollection followers = new OrderedCollection();
+ followers.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString());
+ followers.setTotalItems(userService.getStatsMyReaders(user.getUid()));
+ followers.setFirst(uriComponentsBuilder.path(String.format("/u/%s/followers", userName)).toUriString());
+ return (OrderedCollection) Context.build(followers);
+ }
+ throw new HttpNotFoundException();
+ }
+
+ @GetMapping(value = "/u/{userName}/followers", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
+ public OrderedCollectionPage getFollowersPage(@PathVariable String userName,
+ @RequestParam(required = false, defaultValue = "0") int page) {
+ User user = userService.getUserByName(userName);
+ if (!user.isAnonymous()) {
+ UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri);
+ uriComponentsBuilder.path(String.format("/u/%s/followers", userName));
+ List<User> followers = userService.getUserReaders(user.getUid());
+ Stream<User> followersPage = followers.stream().skip(20 * page).limit(20);
+
+ OrderedCollectionPage result = new OrderedCollectionPage();
+ result.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString());
+ result.setOrderedItems(followersPage.map(a -> {
+ Person follower = new Person();
+ follower.setName(a.getName());
+ follower.setPreferredUsername(a.getName());
+ follower.setUrl(activityPubManager.personWebUri(a));
+ return follower;
+ }).collect(Collectors.toList()));
+ boolean hasNext = followers.size() <= 20 * page;
+ if (hasNext) {
+ result.setNext(uriComponentsBuilder.queryParam("page", page + 1).toUriString());
+ }
+ return (OrderedCollectionPage) Context.build(result);
+ }
+ throw new HttpNotFoundException();
+ }
+
+ @GetMapping(value = "/u/{userName}/following/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
+ public OrderedCollection getFollowing(@PathVariable String userName) {
+ User user = userService.getUserByName(userName);
+ if (!user.isAnonymous()) {
+ UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri);
+ OrderedCollection following = new OrderedCollection();
+ following.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString());
+ following.setTotalItems(userService.getUserFriends(user.getUid()).size());
+ following.setFirst(uriComponentsBuilder.path(String.format("/u/%s/followers", userName)).toUriString());
+ return (OrderedCollection) Context.build(following);
+ }
+ throw new HttpNotFoundException();
+ }
+
+ @GetMapping(value = "/u/{userName}/following", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
+ public OrderedCollectionPage getFollowingPage(@PathVariable String userName,
+ @RequestParam(required = false, defaultValue = "0") int page) {
+ User user = userService.getUserByName(userName);
+ if (!user.isAnonymous()) {
+ UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri);
+ uriComponentsBuilder.path(String.format("/u/%s/following", userName));
+ List<User> following = userService.getUserFriends(user.getUid());
+ Stream<User> followingPage = following.stream().skip(20 * page).limit(20);
+
+ OrderedCollectionPage result = new OrderedCollectionPage();
+ result.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString());
+ result.setOrderedItems(followingPage.map(a -> {
+ Person follower = new Person();
+ follower.setName(a.getName());
+ follower.setPreferredUsername(a.getName());
+ follower.setUrl(activityPubManager.personWebUri(a));
+ return follower;
+ }).collect(Collectors.toList()));
+ boolean hasNext = following.size() <= 20 * page;
+ if (hasNext) {
+ result.setNext(uriComponentsBuilder.queryParam("page", page + 1).toUriString());
+ }
+ return (OrderedCollectionPage) Context.build(result);
+ }
+ throw new HttpNotFoundException();
+ }
+
+ @GetMapping(value = "/n/{mid}-{rid}", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
+ public Context showNote(@PathVariable int mid, @PathVariable int rid) {
+ if (rid > 0) {
+ // reply
+ return Context.build(activityPubManager.makeNote(
+ messagesService.getReply(mid, rid)));
+ }
+ return Context.build(activityPubManager.makeNote(
+ messagesService.getMessage(mid).orElseThrow(IllegalStateException::new)));
+ }
+
+ @PostMapping(value = "/api/inbox", consumes = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE})
+ public ResponseEntity<CommandResult> processInbox(
+ @Visitor User visitor,
+ InputStream inboxData) throws Exception {
+ String inbox = IOUtils.toString(inboxData, StandardCharsets.UTF_8);
+ logger.info("Inbox: {}", inbox);
+ Activity activity = jsonMapper.readValue(inbox, Activity.class);
+ if ((StringUtils.isNotEmpty(visitor.getUri().toString())
+ && visitor.getUri().equals(URI.create(activity.getActor())))
+ || !visitor.isAnonymous()) {
+ if (activity instanceof Follow) {
+ Follow followRequest = (Follow) activity;
+ applicationEventPublisher.publishEvent(
+ new FollowEvent(this, followRequest));
+ return new ResponseEntity<>(CommandResult.fromString("Follow request accepted"), HttpStatus.ACCEPTED);
+
+ }
+ if (activity instanceof Undo) {
+ Map object = (Map) activity.getObject();
+ String objectType = (String) object.get("type");
+ String objectObject = (String) object.get("object");
+ if (objectType.equals("Follow")) {
+ applicationEventPublisher.publishEvent(new UndoFollowEvent(this, activity.getActor(), objectObject));
+ return new ResponseEntity<>(CommandResult.fromString("Undo follow request accepted"), HttpStatus.OK);
+ } else if (objectType.equals("Like") || objectType.equals("Announce")) {
+ applicationEventPublisher.publishEvent(new UndoAnnounceEvent(this, activity.getActor(), objectObject));
+ return new ResponseEntity<>(CommandResult.fromString("Undo like/announce request accepted"), HttpStatus.OK);
+ }
+ }
+ if (activity instanceof Create) {
+ if (activity.getObject() instanceof Map) {
+ Map<String, Object> note = (Map<String, Object>) activity.getObject();
+ if (note.get("type").equals("Note")) {
+ URI noteId = URI.create((String) note.get("id"));
+ if (messagesService.replyExists(noteId)) {
+ return new ResponseEntity<>(CommandResult.fromString("Reply already exists"), HttpStatus.OK);
+ } else {
+ String inReplyTo = (String) note.get("inReplyTo");
+ if (StringUtils.isNotBlank(inReplyTo)) {
+ if (inReplyTo.startsWith(baseUri)) {
+ String postId = activityPubManager.postId(inReplyTo);
+ User user = new User();
+ user.setUri(URI.create(activity.getActor()));
+ String markdown = remarkConverter.convertFragment((String) note.get("content"));
+ String commandBody = note.get("attachment") == null ? markdown :
+ ((List<Object>) note.get("attachment")).stream().map(attachmentObj -> {
+ Map<String, String> attachment = (Map<String, String>) attachmentObj;
+ String attachmentUrl = attachment.get("url");
+ String attachmentName = attachment.get("name");
+ return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName);
+ }).reduce(markdown, (currentUrl, nextUrl) -> String.format("%s\n%s", currentUrl, nextUrl));
+
+ CommandResult result = commandsManager.processCommand(
+ user, String.format("#%s %s", postId, commandBody),
+ URI.create(StringUtils.EMPTY));
+ logger.info(jsonMapper.writeValueAsString(result));
+ if (result.getNewMessage().isPresent()) {
+ messagesService.updateReplyUri(result.getNewMessage().get(), noteId);
+ return new ResponseEntity<>(result, HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
+ }
+ } else {
+ Message reply = messagesService.getReplyByUri(inReplyTo);
+ if (reply != null) {
+ User user = new User();
+ user.setUri(URI.create(activity.getActor()));
+ String markdown = remarkConverter.convertFragment((String)note.get("content"));
+ // combine note text with attachment urls
+ String commandBody = note.get("attachment") == null ? markdown :
+ ((List<Object>) note.get("attachment")).stream().map(attachmentObj -> {
+ Map<String, String> attachment = (Map<String, String>) attachmentObj;
+ String attachmentUrl = attachment.get("url");
+ String attachmentName = attachment.get("name");
+ return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName);
+ }).reduce(markdown, (currentUrl, nextUrl) -> String.format("%s\n%s", currentUrl, nextUrl));
+ CommandResult result = commandsManager.processCommand(
+ user,
+ String.format("#%d/%d %s", reply.getMid(), reply.getRid(), commandBody),
+ URI.create(StringUtils.EMPTY));
+ logger.info(jsonMapper.writeValueAsString(result));
+ if (result.getNewMessage().isPresent()) {
+ messagesService.updateReplyUri(result.getNewMessage().get(), noteId);
+ return new ResponseEntity<>(result, HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (activity instanceof Delete) {
+ if (activity.getObject() instanceof String) {
+ // Delete gone user
+ // TODO: check if it is really deleted and remove copy-paste
+ if (activity.getActor().equals(activity.getObject())) {
+ return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.ACCEPTED);
+ }
+ }
+ Map<String, Object> tombstone = (Map<String, Object>) activity.getObject();
+ if (tombstone.get("type").equals("Tombstone")) {
+ URI actor = URI.create(activity.getActor());
+ URI reply = URI.create((String)tombstone.get("id"));
+ messagesService.deleteReply(actor, reply);
+ return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.OK);
+ }
+ }
+ if (activity instanceof Like || activity instanceof Announce) {
+ String messageUri = activity.getObject() instanceof String ? (String) activity.getObject()
+ : activity.getObject() instanceof Context ? ((Context) activity.getObject()).getId()
+ : (String) ((Map)activity.getObject()).get("id");
+ applicationEventPublisher.publishEvent(new AnnounceEvent(this, activity.getActor(), messageUri));
+ return new ResponseEntity<>(CommandResult.fromString("Like/announce request accepted"), HttpStatus.OK);
+ }
+ logger.warn("Unknown activity: {}", jsonMapper.writeValueAsString(activity));
+ return new ResponseEntity<>(CommandResult.fromString("Unknown activity"), HttpStatus.NOT_IMPLEMENTED);
+ }
+ if (activity instanceof Delete) {
+ if (activity.getObject() instanceof String) {
+ // Delete gone user
+ if (activity.getActor().equals(activity.getObject())) {
+ return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.ACCEPTED);
+ }
+ }
+ }
+ return new ResponseEntity<>(CommandResult.fromString("Can not authenticate"), HttpStatus.UNAUTHORIZED);
+ }
+ @PostMapping(value = "/u/", produces = MediaType.APPLICATION_JSON_VALUE)
+ public User fetchUser(@RequestParam URI uri) {
+ return activityPubManager.personToUser(uri);
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/helpers/ActivityIdDeserializer.java b/src/main/java/com/juick/www/api/activity/helpers/ActivityIdDeserializer.java
new file mode 100644
index 00000000..e349bff7
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/helpers/ActivityIdDeserializer.java
@@ -0,0 +1,38 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.helpers;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+
+public class ActivityIdDeserializer extends JsonDeserializer<String> {
+ @Override
+ public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ JsonToken jsonToken = p.getCurrentToken();
+ if (jsonToken == JsonToken.START_OBJECT) {
+ JsonNode node = p.getCodec().readTree(p);
+ return node.get("id").textValue();
+ }
+ return p.getValueAsString();
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/helpers/LinkValueDeserializer.java b/src/main/java/com/juick/www/api/activity/helpers/LinkValueDeserializer.java
new file mode 100644
index 00000000..ab797f6c
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/helpers/LinkValueDeserializer.java
@@ -0,0 +1,38 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.helpers;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+
+public class LinkValueDeserializer extends JsonDeserializer<String> {
+ @Override
+ public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ JsonToken jsonToken = p.getCurrentToken();
+ if (jsonToken == JsonToken.VALUE_STRING) {
+ return p.getValueAsString();
+ }
+ JsonNode node = p.getCodec().readTree(p);
+ return node.get("href").textValue();
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/Activity.java b/src/main/java/com/juick/www/api/activity/model/Activity.java
new file mode 100644
index 00000000..2ac22349
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/Activity.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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.juick.www.api.activity.helpers.ActivityIdDeserializer;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.UUID;
+
+public abstract class Activity extends Context {
+
+ @JsonDeserialize(using = ActivityIdDeserializer.class)
+ private String actor;
+ private Object object;
+
+ public String getActor() {
+ return actor;
+ }
+
+ public void setActor(String actor) {
+ this.actor = actor;
+ if (StringUtils.isEmpty(getId())) {
+ setId(String.format("%s#%s", this.actor, UUID.randomUUID().toString()));
+ }
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public void setObject(Object object) {
+ this.object = object;
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/Context.java b/src/main/java/com/juick/www/api/activity/model/Context.java
new file mode 100644
index 00000000..57e29e0f
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/Context.java
@@ -0,0 +1,144 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.juick.www.api.activity.helpers.LinkValueDeserializer;
+import com.juick.www.api.activity.model.activities.*;
+import com.juick.www.api.activity.model.objects.*;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property="type")
+@JsonSubTypes({
+ @JsonSubTypes.Type(value = Create.class, name = "Create"),
+ @JsonSubTypes.Type(value = Update.class, name = "Update"),
+ @JsonSubTypes.Type(value = Delete.class, name = "Delete"),
+ @JsonSubTypes.Type(value = Follow.class, name = "Follow"),
+ @JsonSubTypes.Type(value = Accept.class, name = "Accept"),
+ @JsonSubTypes.Type(value = Undo.class, name = "Undo"),
+ @JsonSubTypes.Type(value = Like.class, name = "Like"),
+ @JsonSubTypes.Type(value = Block.class, name = "Block"),
+ @JsonSubTypes.Type(value = Announce.class, name = "Announce"),
+ @JsonSubTypes.Type(value = Activity.class, name = "Activity"),
+ @JsonSubTypes.Type(value = Image.class, name = "Image"),
+ @JsonSubTypes.Type(value = Key.class, name = "Key"),
+ @JsonSubTypes.Type(value = Link.class, name = "Link"),
+ @JsonSubTypes.Type(value = Hashtag.class, name = "Hashtag"),
+ @JsonSubTypes.Type(value = Mention.class, name = "Mention"),
+ @JsonSubTypes.Type(value = Emoji.class, name = "Emoji"),
+ @JsonSubTypes.Type(value = Note.class, name = "Note"),
+ @JsonSubTypes.Type(value = OrderedCollection.class, name = "OrderedCollection"),
+ @JsonSubTypes.Type(value = OrderedCollectionPage.class, name = "OrderedCollectionPage"),
+ @JsonSubTypes.Type(value = Person.class, name = "Person")
+})
+public abstract class Context {
+
+ private List<Object> context;
+
+ private String id;
+
+ private String name;
+
+ private Instant published;
+
+ @JsonDeserialize(using = LinkValueDeserializer.class)
+ private String url;
+
+ private List<String> to;
+
+ private List<Context> tags;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getType() {
+ return getClass().getSimpleName();
+ }
+
+ @JsonProperty("@context")
+ @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+ public List<Object> getContext() {
+ return context;
+ }
+
+ public final static String ACTIVITY_STREAMS_URI = "https://www.w3.org/ns/activitystreams";
+ public final static String SECURITY_URI = "https://w3id.org/security/v1";
+ public final static String LD_JSON_MEDIA_TYPE = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
+ public final static String ACTIVITY_MEDIA_TYPE = "application/activity+json";
+ public final static String ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE = ACTIVITY_MEDIA_TYPE + "; profile=\"https://www.w3.org/ns/activitystreams\"";
+ public final static String ACTIVITYSTREAMS_PUBLIC = "https://www.w3.org/ns/activitystreams#Public";
+
+ public Instant getPublished() {
+ return published;
+ }
+
+ public void setPublished(Instant published) {
+ this.published = published;
+ }
+
+ public List<String> getTo() {
+ return to;
+ }
+
+ public void setTo(List<String> to) {
+ this.to = to;
+ }
+
+ public static Context build(Context response) {
+ response.context = new ArrayList<>(Arrays.asList(ACTIVITY_STREAMS_URI, SECURITY_URI));
+ response.context.add(Collections.singletonMap("Hashtag", "as:Hashtag"));
+ return response;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ @JsonProperty("tag")
+ public List<Context> getTags() {
+ return tags;
+ }
+
+ public void setTags(List<Context> tags) {
+ this.tags = tags;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/activities/Accept.java b/src/main/java/com/juick/www/api/activity/model/activities/Accept.java
new file mode 100644
index 00000000..30a55839
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/activities/Accept.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.activities;
+
+import com.juick.www.api.activity.model.Activity;
+
+public class Accept extends Activity {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/activities/Announce.java b/src/main/java/com/juick/www/api/activity/model/activities/Announce.java
new file mode 100644
index 00000000..4561b10e
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/activities/Announce.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.activities;
+
+import com.juick.www.api.activity.model.Activity;
+
+public class Announce extends Activity {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/activities/Block.java b/src/main/java/com/juick/www/api/activity/model/activities/Block.java
new file mode 100644
index 00000000..66919235
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/activities/Block.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.activities;
+
+import com.juick.www.api.activity.model.Activity;
+
+public class Block extends Activity {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/activities/Create.java b/src/main/java/com/juick/www/api/activity/model/activities/Create.java
new file mode 100644
index 00000000..5da46545
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/activities/Create.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.activities;
+
+import com.juick.www.api.activity.model.Activity;
+
+public class Create extends Activity {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/activities/Delete.java b/src/main/java/com/juick/www/api/activity/model/activities/Delete.java
new file mode 100644
index 00000000..0214f869
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/activities/Delete.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.activities;
+
+import com.juick.www.api.activity.model.Activity;
+
+public class Delete extends Activity {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/activities/Follow.java b/src/main/java/com/juick/www/api/activity/model/activities/Follow.java
new file mode 100644
index 00000000..b7653d23
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/activities/Follow.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.activities;
+
+import com.juick.www.api.activity.model.Activity;
+
+public class Follow extends Activity {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/activities/Like.java b/src/main/java/com/juick/www/api/activity/model/activities/Like.java
new file mode 100644
index 00000000..76a35a3c
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/activities/Like.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.activities;
+
+import com.juick.www.api.activity.model.Activity;
+
+public class Like extends Activity {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/activities/Undo.java b/src/main/java/com/juick/www/api/activity/model/activities/Undo.java
new file mode 100644
index 00000000..0e1c7e8a
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/activities/Undo.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.activities;
+
+import com.juick.www.api.activity.model.Activity;
+
+public class Undo extends Activity {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/activities/Update.java b/src/main/java/com/juick/www/api/activity/model/activities/Update.java
new file mode 100644
index 00000000..d6ca9c1b
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/activities/Update.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.activities;
+
+import com.juick.www.api.activity.model.Activity;
+
+public class Update extends Activity {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Emoji.java b/src/main/java/com/juick/www/api/activity/model/objects/Emoji.java
new file mode 100644
index 00000000..bb556292
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Emoji.java
@@ -0,0 +1,23 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.juick.www.api.activity.model.Context;
+
+public class Emoji extends Context {
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Hashtag.java b/src/main/java/com/juick/www/api/activity/model/objects/Hashtag.java
new file mode 100644
index 00000000..175cc305
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Hashtag.java
@@ -0,0 +1,28 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Hashtag extends Mention {
+ @JsonCreator
+ public Hashtag(@JsonProperty("href") String href, @JsonProperty("name") String name) {
+ super(href, name);
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Image.java b/src/main/java/com/juick/www/api/activity/model/objects/Image.java
new file mode 100644
index 00000000..0dfa6271
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Image.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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.juick.www.api.activity.model.Context;
+
+public class Image extends Context {
+ private String mediaType;
+
+ public String getMediaType() {
+ return mediaType;
+ }
+
+ public void setMediaType(String mediaType) {
+ this.mediaType = mediaType;
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Key.java b/src/main/java/com/juick/www/api/activity/model/objects/Key.java
new file mode 100644
index 00000000..26d60213
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Key.java
@@ -0,0 +1,41 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.juick.www.api.activity.model.Context;
+
+public class Key extends Context {
+ private String owner;
+ private String publicKeyPem;
+
+ public String getOwner() {
+ return owner;
+ }
+
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+
+ public String getPublicKeyPem() {
+ return publicKeyPem;
+ }
+
+ public void setPublicKeyPem(String publicKeyPem) {
+ this.publicKeyPem = publicKeyPem;
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Link.java b/src/main/java/com/juick/www/api/activity/model/objects/Link.java
new file mode 100644
index 00000000..58bf2304
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Link.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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.juick.www.api.activity.model.Context;
+
+public class Link extends Context {
+ private String href;
+
+ public String getHref() {
+ return href;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Mention.java b/src/main/java/com/juick/www/api/activity/model/objects/Mention.java
new file mode 100644
index 00000000..a6099b53
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Mention.java
@@ -0,0 +1,29 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class Mention extends Link {
+ @JsonCreator
+ public Mention(@JsonProperty("href") String href, @JsonProperty("name") String name) {
+ this.setHref(href);
+ this.setName(name);
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Note.java b/src/main/java/com/juick/www/api/activity/model/objects/Note.java
new file mode 100644
index 00000000..b04b1ec9
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Note.java
@@ -0,0 +1,81 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.juick.www.api.activity.model.Context;
+
+import java.util.List;
+
+public class Note extends Context {
+ private String content;
+ private String attributedTo;
+ private String inReplyTo;
+ private List<Image> attachment;
+ private List<String> to;
+ private List<String> cc;
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public String getAttributedTo() {
+ return attributedTo;
+ }
+
+ public void setAttributedTo(String attributedTo) {
+ this.attributedTo = attributedTo;
+ }
+
+ @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+ public List<Image> getAttachment() {
+ return attachment;
+ }
+
+ public void setAttachment(List<Image> attachment) {
+ this.attachment = attachment;
+ }
+
+ public List<String> getTo() {
+ return to;
+ }
+
+ public void setTo(List<String> to) {
+ this.to = to;
+ }
+
+ public List<String> getCc() {
+ return cc;
+ }
+
+ public void setCc(List<String> cc) {
+ this.cc = cc;
+ }
+
+ public String getInReplyTo() {
+ return inReplyTo;
+ }
+
+ public void setInReplyTo(String inReplyTo) {
+ this.inReplyTo = inReplyTo;
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/OrderedCollection.java b/src/main/java/com/juick/www/api/activity/model/objects/OrderedCollection.java
new file mode 100644
index 00000000..d7d43a26
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/OrderedCollection.java
@@ -0,0 +1,42 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.juick.www.api.activity.model.Context;
+
+public class OrderedCollection extends Context {
+
+ private int totalItems;
+
+ public int getTotalItems() {
+ return totalItems;
+ }
+
+ public void setTotalItems(int totalItems) {
+ this.totalItems = totalItems;
+ }
+ private String first;
+
+ public String getFirst() {
+ return first;
+ }
+
+ public void setFirst(String first) {
+ this.first = first;
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/OrderedCollectionPage.java b/src/main/java/com/juick/www/api/activity/model/objects/OrderedCollectionPage.java
new file mode 100644
index 00000000..340e24cf
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/OrderedCollectionPage.java
@@ -0,0 +1,75 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.juick.www.api.activity.model.Context;
+
+import java.util.List;
+
+public class OrderedCollectionPage extends Context {
+
+ private String partOf;
+
+ private String first;
+
+ private String next;
+
+ private String last;
+
+ private List<? extends Context> orderedItems;
+
+ public String getNext() {
+ return next;
+ }
+
+ public void setNext(String next) {
+ this.next = next;
+ }
+
+ public List<? extends Context> getOrderedItems() {
+ return orderedItems;
+ }
+
+ public void setOrderedItems(List<? extends Context> orderedItems) {
+ this.orderedItems = orderedItems;
+ }
+
+ public String getPartOf() {
+ return partOf;
+ }
+
+ public void setPartOf(String partOf) {
+ this.partOf = partOf;
+ }
+
+ public String getFirst() {
+ return first;
+ }
+
+ public void setFirst(String first) {
+ this.first = first;
+ }
+
+ public String getLast() {
+ return last;
+ }
+
+ public void setLast(String last) {
+ this.last = last;
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Person.java b/src/main/java/com/juick/www/api/activity/model/objects/Person.java
new file mode 100644
index 00000000..c653bee1
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Person.java
@@ -0,0 +1,104 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.juick.www.api.activity.model.Context;
+
+public class Person extends Context {
+
+ private String name;
+ private String preferredUsername;
+ private Image icon;
+ private String inbox;
+ private String outbox;
+ private String following;
+ private String followers;
+ private Key publicKey;
+
+ @Override
+ public String getType() {
+ return "Person";
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+ public Image getIcon() {
+ return icon;
+ }
+
+ public void setIcon(Image icon) {
+ this.icon = icon;
+ }
+
+ public String getOutbox() {
+ return outbox;
+ }
+
+ public void setOutbox(String outbox) {
+ this.outbox = outbox;
+ }
+
+ public String getInbox() {
+ return inbox;
+ }
+
+ public void setInbox(String inbox) {
+ this.inbox = inbox;
+ }
+
+ public String getFollowing() {
+ return following;
+ }
+
+ public void setFollowing(String following) {
+ this.following = following;
+ }
+
+ public String getFollowers() {
+ return followers;
+ }
+
+ public void setFollowers(String followers) {
+ this.followers = followers;
+ }
+
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+ public Key getPublicKey() {
+ return publicKey;
+ }
+
+ public void setPublicKey(Key publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ public String getPreferredUsername() {
+ return preferredUsername;
+ }
+
+ public void setPreferredUsername(String preferredUsername) {
+ this.preferredUsername = preferredUsername;
+ }
+}