package managers; import models.Account; import models.Group; import models.Post; import models.enums.GroupType; import models.enums.LinkType; import models.services.ElasticsearchService; import play.Configuration; import play.db.jpa.JPA; import play.db.jpa.JPAApi; import javax.inject.Inject; import javax.persistence.Query; import java.io.IOException; import java.util.*; /** * Created by Iven on 17.12.2015. */ public class PostManager implements BaseManager { @Inject ElasticsearchService elasticsearchService; @Inject NotificationManager notificationManager; @Inject FriendshipManager friendshipManager; @Inject GroupAccountManager groupAccountManager; @Inject PostBookmarkManager postBookmarkManager; @Inject GroupManager groupManager; @Inject Configuration configuration; @Inject JPAApi jpaApi; @Override public void create(Object model) { Post post = (Post) model; jpaApi.em().persist(post); try { elasticsearchService.index(post); } catch (IOException e) { e.printStackTrace(); } } public void createWithoutIndex(Post post) { jpaApi.em().persist(post); } @Override public void update(Object model) { ((Post) model).updatedAt(); } @Override public void delete(Object model) { Post post = (Post) model; // delete all comments first List<Post> comments = getCommentsForPost(post.id, 0, 0); for (Post comment : comments) { delete(comment); } notificationManager.deleteReferences(post); jpaApi.em().remove(post); // Delete Elasticsearch document elasticsearchService.delete(post); } @SuppressWarnings("unchecked") public List<Post> getCommentsForPost(Long id, int limit, int offset) { Query query = jpaApi.em() .createQuery("SELECT p FROM Post p WHERE p.parent.id = ?1 ORDER BY p.createdAt ASC") .setParameter(1, id); query = limit(query, limit, offset); return (List<Post>) query.getResultList(); } @SuppressWarnings("unchecked") public static List<Post> getCommentsForPost2(Long id, int limit, int offset) { Query query = JPA.em() .createQuery("SELECT p FROM Post p WHERE p.parent.id = ?1 ORDER BY p.createdAt ASC") .setParameter(1, id); query = limit(query, limit, offset); return (List<Post>) query.getResultList(); } @SuppressWarnings("unchecked") public List<Post> getPostsForGroup(final Group group, final int limit, final int page) { Query query = jpaApi.em() .createQuery("SELECT p FROM Post p WHERE p.group.id = ?1 ORDER BY p.updatedAt DESC") .setParameter(1, group.id); int offset = (page * limit) - limit; query = limit(query, limit, offset); return query.getResultList(); } private static Query limit(Query query, int limit, int offset) { if (limit > 0) { query.setMaxResults(limit); } if (offset >= 0) { query.setFirstResult(offset); } return query; } public Post findById(Long id) { return jpaApi.em().find(Post.class, id); } public int countPostsForGroup(final Group group) { return ((Number) jpaApi.em().createQuery("SELECT COUNT(p) FROM Post p WHERE p.group.id = ?1").setParameter(1, group.id).getSingleResult()).intValue(); } @SuppressWarnings("unchecked") public List<Post> findStreamForAccount(final Account account, final List<Group> groupList, final List<Account> friendList, final List<Post> bookmarkList, final String filter, final int limit, final int offset) { Query query = streamForAccount("SELECT DISTINCT p ", account, groupList, friendList, bookmarkList, filter, " ORDER BY p.updatedAt DESC"); // set limit and offset query = limit(query, limit, offset); return query.getResultList(); } public int countStreamForAccount(final Account account, final List<Group> groupList, final List<Account> friendList, final List<Post> bookmarkList, final String filter) { final Query query = streamForAccount("SELECT DISTINCT COUNT(p)", account, groupList, friendList, bookmarkList, filter, ""); return ((Number) query.getSingleResult()).intValue(); } /** * @param account - Account (usually: current user or a contact) * @param groupList - a list containing all groups we want to search in (usually all groups from account) * @param accountList - a list containing all accounts we want to search in (usually contact from account) * @return List of Posts */ public Query streamForAccount(String selectClause, Account account, List<Group> groupList, List<Account> accountList, List<Post> bookmarkList, String filter, String orderByClause) { HashMap<String, String> streamClausesMap = new HashMap<>(); List<String> streamClausesList = new ArrayList<>(); // find stream posts from @account String accountPosts = " (p.owner = (:account) AND p.account = (:account)) "; streamClausesMap.put("accountPosts", accountPosts); if (groupList != null && groupList.size() != 0) { // find group posts from @account String accountGroupPosts = " (p.owner = (:account) AND p.group IN (:groupList)) "; streamClausesMap.put("accountGroupPosts", accountGroupPosts); // find posts from each group in @groupList String allGroupPosts = " (p.group IN (:groupList)) "; streamClausesMap.put("allGroupPosts", allGroupPosts); } if (accountList != null && accountList.size() != 0) { // find posts from @account where @account posted on @accountList String accountContactPosts = " (p.owner = (:account) AND p.account IN (:accountList)) "; streamClausesMap.put("accountContactPosts", accountContactPosts); // find posts from @accountList where @accountList posted on @account's feed String contactToAccountPosts = " (p.owner IN (:accountList) AND p.account = (:account)) "; streamClausesMap.put("contactToAccountPosts", contactToAccountPosts); // find posts from @accountList which are posted on his/her own feed String contactPosts = " (p.owner IN (:accountList) AND p.account = p.owner) "; streamClausesMap.put("contactPosts", contactPosts); } if (bookmarkList != null && bookmarkList.size() != 0) { // find bookmarked posts from @account String bookmarkPosts = " (p IN (:bookmarkList)) "; streamClausesMap.put("bookmarkPosts", bookmarkPosts); } switch (filter) { case "group": streamClausesList.add(streamClausesMap.get("allGroupPosts")); break; case "account": streamClausesList.add(streamClausesMap.get("accountPosts")); streamClausesList.add(streamClausesMap.get("accountContactPosts")); streamClausesList.add(streamClausesMap.get("accountGroupPosts")); break; case "contact": streamClausesList.add(streamClausesMap.get("contactToAccountPosts")); streamClausesList.add(streamClausesMap.get("contactPosts")); break; case "visitor": streamClausesList.add(streamClausesMap.get("accountGroupPosts")); streamClausesList.add(streamClausesMap.get("contactToAccountPosts")); streamClausesList.add(streamClausesMap.get("accountPosts")); break; case "bookmark": streamClausesList.add(streamClausesMap.get("bookmarkPosts")); break; default: streamClausesList.add(streamClausesMap.get("accountPosts")); streamClausesList.add(streamClausesMap.get("accountGroupPosts")); streamClausesList.add(streamClausesMap.get("allGroupPosts")); streamClausesList.add(streamClausesMap.get("accountContactPosts")); streamClausesList.add(streamClausesMap.get("contactToAccountPosts")); streamClausesList.add(streamClausesMap.get("contactPosts")); break; } // its possible that @streamClausesList contains null values. remove them. streamClausesList.removeAll(Collections.singleton(null)); // assemble query. // insert dummy where clause (1=2) for the unlikely event of empty @streamClausesList (e.g. new user with no groups or contact) String completeQuery = selectClause + " FROM Post p WHERE 1=2 " + assembleClauses(streamClausesList) + orderByClause; Query query = jpaApi.em().createQuery(completeQuery); // check @completeQuery for parameter which are needed. // () are necessary to distinguish between :account and :accountList if (completeQuery.contains("(:account)")) query.setParameter("account", account); if (completeQuery.contains("(:groupList)")) query.setParameter("groupList", groupList); if (completeQuery.contains("(:accountList)")) query.setParameter("accountList", accountList); if (completeQuery.contains("(:bookmarkList)")) query.setParameter("bookmarkList", bookmarkList); return query; } private static String assembleClauses(List<String> streamClausesList) { String assembledClauses = ""; Iterator iterator = streamClausesList.iterator(); while (iterator.hasNext()) { assembledClauses += " OR " + iterator.next().toString(); } return assembledClauses; } public int countCommentsForPost(final Long id) { return ((Number) jpaApi.em().createQuery("SELECT COUNT(p.id) FROM Post p WHERE p.parent.id = ?1").setParameter(1, id).getSingleResult()).intValue(); } public static int countCommentsForPost2(final Long id) { return ((Number) JPA.em().createQuery("SELECT COUNT(p.id) FROM Post p WHERE p.parent.id = ?1").setParameter(1, id).getSingleResult()).intValue(); } /** * @param account Account (current user) * @return List of Posts */ public List<Post> getStream(Account account, int limit, int page) { // find friends and groups of given account List<Account> friendList = friendshipManager.findFriends(account); List<Group> groupList = groupAccountManager.findEstablished(account); List<Post> bookmarkList = postBookmarkManager.findByAccount(account); int offset = (page * limit) - limit; return findStreamForAccount(account, groupList, friendList, bookmarkList, "all", limit, offset); } /** * @param account Account (current user) * @return List of Posts */ public List<Post> getFilteredStream(Account account, int limit, int page, String filter) { int offset = (page * limit) - limit; return findStreamForAccount(account, groupAccountManager.findEstablished(account), friendshipManager.findFriends(account), postBookmarkManager.findByAccount(account), filter, limit, offset); } /** * @param account Account (current user) * @return Number of Posts */ public int countStream(Account account, String filter) { return countStreamForAccount(account, groupAccountManager.findEstablished(account), friendshipManager.findFriends(account), postBookmarkManager.findByAccount(account), filter); } /** * @param contact - Account * @return List of Posts */ public List<Post> getFriendStream(Account contact, int limit, int page) { int offset = (page * limit) - limit; return findStreamForAccount(contact, groupAccountManager.findPublicEstablished(contact), friendshipManager.findFriends(contact), postBookmarkManager.findByAccount(contact), "visitor", limit, offset); } /** * @param contact - Account (a friends account) * @return Number of Posts */ public int countFriendStream(Account contact) { return countStreamForAccount(contact, groupAccountManager.findPublicEstablished(contact), friendshipManager.findFriends(contact), postBookmarkManager.findByAccount(contact), "visitor"); } public static int getCountComments(Post post) { return countCommentsForPost2(post.id); } public boolean belongsToGroup(Post post) { return post.group != null; } public boolean belongsToAccount(Post post) { return post.account != null; } public boolean belongsToPost(Post post) { return post.parent != null; } public boolean isMine(Post post) { return post.account.equals(post.owner); } /** * Collect all AccountIds, which are able to view this.post * * @return List of AccountIds */ public Set<Long> findAllowedToViewAccountIds(Post post) { Set<Long> viewableIds = new HashSet<>(); // everybody from post.group can see this post if (belongsToGroup(post)) { viewableIds.addAll(groupAccountManager.findAccountIdsByGroup(post.group, LinkType.establish)); } if (belongsToAccount(post)) { // every friend from post.account can see this post viewableIds.addAll(friendshipManager.findFriendsId(post.account)); // the owner of this.account can see this post viewableIds.add(post.account.id); // everybody can see his own post if (isMine(post)) { viewableIds.add(post.owner.id); } } // multiple options if post is a comment if (belongsToPost(post)) { // every member from post.parent.group can see this post if (belongsToGroup(post.parent)) { viewableIds.addAll(groupAccountManager.findAccountIdsByGroup(post.parent.group, LinkType.establish)); } // every friend from post.parent.account can see this post if (belongsToAccount(post.parent)) { viewableIds.addAll(friendshipManager.findFriendsId(post.parent.account)); // everybody can see his own comment if (isMine(post.parent)) { viewableIds.add(post.owner.id); } } } return viewableIds; } public boolean isPublic(Post post) { // post in public group if (belongsToGroup(post)) { return post.group.groupType.equals(GroupType.open); } // comment in public group if (belongsToPost(post) && belongsToGroup(post.parent)) { return post.parent.group.groupType.equals(GroupType.open); } return false; } public long indexAllPosts() throws IOException { final long start = System.currentTimeMillis(); for (Post post : allWithoutExceptionPosts()) elasticsearchService.index(post); return (System.currentTimeMillis() - start) / 1000; } /** * Get all posts except error posts (from Admin) * * @return */ @SuppressWarnings("unchecked") private List<Post> allWithoutExceptionPosts() { Group group = groupManager.findByTitle(configuration.getString("htwplus.admin.group")); Account adminAccount = group.owner; return jpaApi.em().createQuery("FROM Post p WHERE p.owner.id != :adminId AND p.group.id != :groupId OR p.group IS NULL") .setParameter("groupId", group.id) .setParameter("adminId", adminAccount.id) .getResultList(); } /** * Get all posts owned by a specific user * * @return */ @SuppressWarnings("unchecked") public List<Post> listAllPostsOwnedBy(Long id) { return jpaApi.em().createQuery("FROM Post p WHERE p.owner.id = " + id).getResultList(); } /** * get a list of posts posted on the wall of the specified account */ @SuppressWarnings("unchecked") public List<Post> listAllPostsPostedOnAccount(Long id) { return jpaApi.em().createQuery("FROM Post p WHERE p.account.id = " + id).getResultList(); } }