diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..51c40faf --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +sudo: required +dist: trusty +language: java +before_install: + - sudo apt-get -qq update + - sudo apt-get -y -q install tesseract-ocr tesseract-ocr-fra tesseract-ocr-jpn + - sudo apt-get -y -q install haveged && sudo service haveged start +env: + global: + - TESSDATA_PREFIX=/usr/share/tesseract-ocr + - LC_NUMERIC=C \ No newline at end of file diff --git a/README.md b/README.md index 5b8898a1..d2f9eb4f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Sismics Docs +Sismics Docs [![Build Status](https://secure.travis-ci.org/sismics/docs.png)](http://travis-ci.org/sismics/docs) ============ _Web interface_ @@ -50,7 +50,6 @@ Prerequisites: JDK 7 with JCE, Maven 3, Tesseract 3.02 Docs is organized in several Maven modules: - - docs-parent - docs-core - docs-web - docs-web-common @@ -60,7 +59,7 @@ or download the sources from GitHub. #### Launch the build -From the `docs-parent` directory: +From the root directory: mvn clean -DskipTests install diff --git a/docs-core/pom.xml b/docs-core/pom.xml index eeb23ba9..3c1730c1 100644 --- a/docs-core/pom.xml +++ b/docs-core/pom.xml @@ -6,7 +6,7 @@ com.sismics.docs docs-parent 1.0-SNAPSHOT - ../docs-parent + .. 4.0.0 diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/AclDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/AclDao.java index 96989163..7ecbf61c 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/AclDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/AclDao.java @@ -28,7 +28,6 @@ public class AclDao { * @param acl ACL * @param userId User ID * @return New ID - * @throws Exception */ public String create(Acl acl, String userId) { // Create the UUID @@ -82,7 +81,7 @@ public class AclDao { List l = q.getResultList(); // Assemble results - List aclDtoList = new ArrayList(); + List aclDtoList = new ArrayList<>(); for (Object[] o : l) { int i = 0; AclDto aclDto = new AclDto(); @@ -92,7 +91,7 @@ public class AclDao { String userName = (String) o[i++]; String shareId = (String) o[i++]; String shareName = (String) o[i++]; - String groupName = (String) o[i++]; + String groupName = (String) o[i]; if (userName != null) { aclDto.setTargetName(userName); aclDto.setTargetType(AclTargetType.USER.name()); @@ -114,23 +113,25 @@ public class AclDao { * Check if a source is accessible to a target. * * @param sourceId ACL source entity ID - * @parm perm Necessary permission - * @param targetId ACL target entity ID + * @param perm Necessary permission + * @param targetIdList List of targets * @return True if the document is accessible */ public boolean checkPermission(String sourceId, PermType perm, List targetIdList) { EntityManager em = ThreadLocalContext.get().getEntityManager(); - Query q = em.createQuery("select a from Acl a where a.sourceId = :sourceId and a.perm = :perm and a.targetId in (:targetIdList) and a.deleteDate is null"); + StringBuilder sb = new StringBuilder("select a.ACL_ID_C from T_ACL a "); + sb.append(" where a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = :sourceId and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null "); + sb.append(" union all "); + sb.append(" select a.ACL_ID_C from T_ACL a, T_DOCUMENT_TAG dt "); + sb.append(" where a.ACL_SOURCEID_C = dt.DOT_IDTAG_C and dt.DOT_IDDOCUMENT_C = :sourceId and dt.DOT_DELETEDATE_D is null "); + sb.append(" and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null "); + Query q = em.createNativeQuery(sb.toString()); q.setParameter("sourceId", sourceId); - q.setParameter("perm", perm); + q.setParameter("perm", perm.name()); q.setParameter("targetIdList", targetIdList); - + // We have a matching permission - if (q.getResultList().size() > 0) { - return true; - } - - return false; + return q.getResultList().size() > 0; } /** diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java index 95c9b579..403f2ee2 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/DocumentDao.java @@ -40,7 +40,6 @@ public class DocumentDao { * @param document Document * @param userId User ID * @return New ID - * @throws Exception */ public String create(Document document, String userId) { // Create the UUID @@ -87,10 +86,15 @@ public class DocumentDao { * * @param id Document ID * @param perm Permission needed - * @param userId User ID + * @param targetIdList List of targets * @return Document */ public DocumentDto getDocument(String id, PermType perm, List targetIdList) { + AclDao aclDao = new AclDao(); + if (!aclDao.checkPermission(id, perm, targetIdList)) { + return null; + } + EntityManager em = ThreadLocalContext.get().getEntityManager(); StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C, d.DOC_TITLE_C, d.DOC_DESCRIPTION_C, d.DOC_SUBJECT_C, d.DOC_IDENTIFIER_C, d.DOC_PUBLISHER_C, d.DOC_FORMAT_C, d.DOC_SOURCE_C, d.DOC_TYPE_C, d.DOC_COVERAGE_C, d.DOC_RIGHTS_C, d.DOC_CREATEDATE_D, d.DOC_LANGUAGE_C, "); sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null), "); @@ -98,15 +102,12 @@ public class DocumentDao { sb.append(" u.USE_USERNAME_C "); sb.append(" from T_DOCUMENT d "); sb.append(" join T_USER u on d.DOC_IDUSER_C = u.USE_ID_C "); - sb.append(" left join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = :perm and a.ACL_DELETEDATE_D is null "); - sb.append(" where d.DOC_ID_C = :id and a.ACL_ID_C is not null and d.DOC_DELETEDATE_D is null "); + sb.append(" where d.DOC_ID_C = :id and d.DOC_DELETEDATE_D is null "); Query q = em.createNativeQuery(sb.toString()); q.setParameter("id", id); - q.setParameter("perm", perm.name()); - q.setParameter("targetIdList", targetIdList); - - Object[] o = null; + + Object[] o; try { o = (Object[]) q.getSingleResult(); } catch (NoResultException e) { @@ -130,7 +131,7 @@ public class DocumentDao { documentDto.setLanguage((String) o[i++]); documentDto.setShared(((Number) o[i++]).intValue() > 0); documentDto.setFileCount(((Number) o[i++]).intValue()); - documentDto.setCreator((String) o[i++]); + documentDto.setCreator((String) o[i]); return documentDto; } @@ -200,22 +201,24 @@ public class DocumentDao { * @param paginatedList List of documents (updated by side effects) * @param criteria Search criteria * @param sortCriteria Sort criteria - * @return List of documents - * @throws Exception + * @throws Exception */ public void findByCriteria(PaginatedList paginatedList, DocumentCriteria criteria, SortCriteria sortCriteria) throws Exception { - Map parameterMap = new HashMap(); - List criteriaList = new ArrayList(); + Map parameterMap = new HashMap<>(); + List criteriaList = new ArrayList<>(); StringBuilder sb = new StringBuilder("select distinct d.DOC_ID_C c0, d.DOC_TITLE_C c1, d.DOC_DESCRIPTION_C c2, d.DOC_CREATEDATE_D c3, d.DOC_LANGUAGE_C c4, "); sb.append(" (select count(s.SHA_ID_C) from T_SHARE s, T_ACL ac where ac.ACL_SOURCEID_C = d.DOC_ID_C and ac.ACL_TARGETID_C = s.SHA_ID_C and ac.ACL_DELETEDATE_D is null and s.SHA_DELETEDATE_D is null) c5, "); sb.append(" (select count(f.FIL_ID_C) from T_FILE f where f.FIL_DELETEDATE_D is null and f.FIL_IDDOC_C = d.DOC_ID_C) c6 "); sb.append(" from T_DOCUMENT d "); - // Adds search criteria + // Add search criterias if (criteria.getTargetIdList() != null) { // Read permission is enough for searching - sb.append(" join T_ACL a on a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_TARGETID_C in (:targetIdList) and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null "); + sb.append(" left join T_ACL a on a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = d.DOC_ID_C and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null "); + sb.append(" left join T_DOCUMENT_TAG dta on dta.DOT_IDDOCUMENT_C = d.DOC_ID_C and dta.DOT_DELETEDATE_D is null "); + sb.append(" left join T_ACL a2 on a2.ACL_TARGETID_C in (:targetIdList) and a2.ACL_SOURCEID_C = dta.DOT_IDTAG_C and a2.ACL_PERM_C = 'READ' and a2.ACL_DELETEDATE_D is null "); + criteriaList.add("(a.ACL_ID_C is not null or a2.ACL_ID_C is not null)"); parameterMap.put("targetIdList", criteria.getTargetIdList()); } if (!Strings.isNullOrEmpty(criteria.getSearch()) || !Strings.isNullOrEmpty(criteria.getFullSearch())) { @@ -239,7 +242,7 @@ public class DocumentDao { if (criteria.getTagIdList() != null && !criteria.getTagIdList().isEmpty()) { int index = 0; for (String tagId : criteria.getTagIdList()) { - sb.append(" join T_DOCUMENT_TAG dt" + index + " on dt" + index + ".DOT_IDDOCUMENT_C = d.DOC_ID_C and dt" + index + ".DOT_IDTAG_C = :tagId" + index + " and dt" + index + ".DOT_DELETEDATE_D is null "); + sb.append(String.format(" join T_DOCUMENT_TAG dt%d on dt%d.DOT_IDDOCUMENT_C = d.DOC_ID_C and dt%d.DOT_IDTAG_C = :tagId%d and dt%d.DOT_DELETEDATE_D is null ", index, index, index, index, index)); parameterMap.put("tagId" + index, tagId); index++; } @@ -278,7 +281,7 @@ public class DocumentDao { documentDto.setCreateTimestamp(((Timestamp) o[i++]).getTime()); documentDto.setLanguage((String) o[i++]); documentDto.setShared(((Number) o[i++]).intValue() > 0); - documentDto.setFileCount(((Number) o[i++]).intValue()); + documentDto.setFileCount(((Number) o[i]).intValue()); documentDtoList.add(documentDto); } diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/GroupDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/GroupDao.java index a4f6735b..b15e8934 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/GroupDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/GroupDao.java @@ -71,7 +71,6 @@ public class GroupDao { * @param group Group * @param userId User ID * @return New ID - * @throws Exception */ public String create(Group group, String userId) { // Create the UUID @@ -127,9 +126,8 @@ public class GroupDao { /** * Add an user to a group. * - * @param group Group + * @param userGroup User group * @return New ID - * @throws Exception */ public String addMember(UserGroup userGroup) { // Create the UUID @@ -170,8 +168,8 @@ public class GroupDao { * @return List of groups */ public List findByCriteria(GroupCriteria criteria, SortCriteria sortCriteria) { - Map parameterMap = new HashMap(); - List criteriaList = new ArrayList(); + Map parameterMap = new HashMap<>(); + List criteriaList = new ArrayList<>(); StringBuilder sb = new StringBuilder("select g.GRP_ID_C as c0, g.GRP_NAME_C as c1, g.GRP_IDPARENT_C as c2, gp.GRP_NAME_C as c3, g.GRP_IDROLE_C "); if (criteria.getUserId() != null) { @@ -187,8 +185,8 @@ public class GroupDao { } if (criteria.getUserId() != null) { // Left join and post-filtering for recursive groups - sb.append((criteria.isRecursive() ? " left " : "") - + " join T_USER_GROUP ug on ug.UGP_IDGROUP_C = g.GRP_ID_C and ug.UGP_IDUSER_C = :userId and ug.UGP_DELETEDATE_D is null "); + sb.append(criteria.isRecursive() ? " left " : ""); + sb.append(" join T_USER_GROUP ug on ug.UGP_IDGROUP_C = g.GRP_ID_C and ug.UGP_IDUSER_C = :userId and ug.UGP_DELETEDATE_D is null "); parameterMap.put("userId", criteria.getUserId()); } @@ -216,7 +214,7 @@ public class GroupDao { .setParentName((String) o[i++]) .setRoleId((String) o[i++]); groupDtoList.add(groupDto); - if (criteria.getUserId() != null && o[i++] != null) { + if (criteria.getUserId() != null && o[i] != null) { userGroupDtoList.add(groupDto); } } diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java index 66f3e20d..a00ee16a 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java @@ -1,21 +1,24 @@ package com.sismics.docs.core.dao.jpa; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.Query; +import com.google.common.base.Joiner; import com.sismics.docs.core.constant.AuditLogType; +import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria; +import com.sismics.docs.core.dao.jpa.criteria.TagCriteria; +import com.sismics.docs.core.dao.jpa.dto.GroupDto; import com.sismics.docs.core.dao.jpa.dto.TagDto; import com.sismics.docs.core.dao.jpa.dto.TagStatDto; import com.sismics.docs.core.model.jpa.DocumentTag; import com.sismics.docs.core.model.jpa.Tag; import com.sismics.docs.core.util.AuditLogUtil; +import com.sismics.docs.core.util.jpa.QueryParam; +import com.sismics.docs.core.util.jpa.QueryUtil; +import com.sismics.docs.core.util.jpa.SortCriteria; import com.sismics.util.context.ThreadLocalContext; /** @@ -39,19 +42,6 @@ public class TagDao { } } - /** - * Returns the list of all tags. - * - * @return List of tags - */ - @SuppressWarnings("unchecked") - public List getByUserId(String userId) { - EntityManager em = ThreadLocalContext.get().getEntityManager(); - Query q = em.createQuery("select t from Tag t where t.userId = :userId and t.deleteDate is null order by t.name"); - q.setParameter("userId", userId); - return q.getResultList(); - } - /** * Update tags on a document. * @@ -93,78 +83,6 @@ public class TagDao { } } } - - /** - * Returns tag list on a document. - * - * @param documentId Document ID - * @return List of tags - */ - @SuppressWarnings("unchecked") - public List getByDocumentId(String documentId, String userId) { - EntityManager em = ThreadLocalContext.get().getEntityManager(); - StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C, t.TAG_IDPARENT_C from T_DOCUMENT_TAG dt "); - sb.append(" join T_TAG t on t.TAG_ID_C = dt.DOT_IDTAG_C "); - sb.append(" where dt.DOT_IDDOCUMENT_C = :documentId and t.TAG_DELETEDATE_D is null "); - sb.append(" and t.TAG_IDUSER_C = :userId and dt.DOT_DELETEDATE_D is null "); - sb.append(" order by t.TAG_NAME_C "); - - // Perform the query - Query q = em.createNativeQuery(sb.toString()); - q.setParameter("documentId", documentId); - q.setParameter("userId", userId); - List l = q.getResultList(); - - // Assemble results - List tagDtoList = new ArrayList<>(); - for (Object[] o : l) { - int i = 0; - TagDto tagDto = new TagDto(); - tagDto.setId((String) o[i++]); - tagDto.setName((String) o[i++]); - tagDto.setColor((String) o[i++]); - tagDto.setParentId((String) o[i]); - tagDtoList.add(tagDto); - } - return tagDtoList; - } - - /** - * Returns stats on tags. - * - * @param userId User ID - * @return Stats by tag - */ - @SuppressWarnings("unchecked") - public List getStats(String userId) { - EntityManager em = ThreadLocalContext.get().getEntityManager(); - StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_C, t.TAG_COLOR_C, t.TAG_IDPARENT_C, count(d.DOC_ID_C) "); - sb.append(" from T_TAG t "); - sb.append(" left join T_DOCUMENT_TAG dt on t.TAG_ID_C = dt.DOT_IDTAG_C and dt.DOT_DELETEDATE_D is null "); - sb.append(" left join T_DOCUMENT d on d.DOC_ID_C = dt.DOT_IDDOCUMENT_C and d.DOC_DELETEDATE_D is null and d.DOC_IDUSER_C = :userId "); - sb.append(" where t.TAG_IDUSER_C = :userId and t.TAG_DELETEDATE_D is null "); - sb.append(" group by t.TAG_ID_C "); - sb.append(" order by t.TAG_NAME_C "); - - // Perform the query - Query q = em.createNativeQuery(sb.toString()); - q.setParameter("userId", userId); - List l = q.getResultList(); - - // Assemble results - List tagStatDtoList = new ArrayList<>(); - for (Object[] o : l) { - int i = 0; - TagStatDto tagDto = new TagStatDto(); - tagDto.setId((String) o[i++]); - tagDto.setName((String) o[i++]); - tagDto.setColor((String) o[i++]); - tagDto.setParentId((String) o[i++]); - tagDto.setCount(((Number) o[i]).intValue()); - tagStatDtoList.add(tagDto); - } - return tagStatDtoList; - } /** * Creates a new tag. @@ -187,44 +105,6 @@ public class TagDao { return tag.getId(); } - - /** - * Returns a tag by name. - * - * @param userId User ID - * @param name Name - * @return Tag - */ - public Tag getByName(String userId, String name) { - EntityManager em = ThreadLocalContext.get().getEntityManager(); - Query q = em.createQuery("select t from Tag t where t.name = :name and t.userId = :userId and t.deleteDate is null"); - q.setParameter("userId", userId); - q.setParameter("name", name); - try { - return (Tag) q.getSingleResult(); - } catch (NoResultException e) { - return null; - } - } - - /** - * Returns a tag by ID. - * - * @param userId User ID - * @param tagId Tag ID - * @return Tag - */ - public Tag getByTagId(String userId, String tagId) { - EntityManager em = ThreadLocalContext.get().getEntityManager(); - Query q = em.createQuery("select t from Tag t where t.id = :tagId and t.userId = :userId and t.deleteDate is null"); - q.setParameter("userId", userId); - q.setParameter("tagId", tagId); - try { - return (Tag) q.getSingleResult(); - } catch (NoResultException e) { - return null; - } - } /** * Deletes a tag. @@ -249,25 +129,15 @@ public class TagDao { q.setParameter("dateNow", dateNow); q.setParameter("tagId", tagId); q.executeUpdate(); + + q = em.createQuery("update Acl a set a.deleteDate = :dateNow where a.sourceId = :tagId and a.deleteDate is null"); + q.setParameter("tagId", tagId); + q.setParameter("dateNow", dateNow); + q.executeUpdate(); // Create audit log AuditLogUtil.create(tagDb, AuditLogType.DELETE, userId); } - - /** - * Search tags by name. - * - * @param name Tag name - * @return List of found tags - */ - @SuppressWarnings("unchecked") - public List findByName(String userId, String name) { - EntityManager em = ThreadLocalContext.get().getEntityManager(); - Query q = em.createQuery("select t from Tag t where t.name like :name and t.userId = :userId and t.deleteDate is null"); - q.setParameter("userId", userId); - q.setParameter("name", "%" + name + "%"); - return q.getResultList(); - } /** * Update a tag. @@ -294,5 +164,72 @@ public class TagDao { return tagFromDb; } + + /** + * Returns the list of all tags. + * + * @param criteria Search criteria + * @param sortCriteria Sort criteria + * @return List of groups + */ + public List findByCriteria(TagCriteria criteria, SortCriteria sortCriteria) { + Map parameterMap = new HashMap<>(); + List criteriaList = new ArrayList<>(); + + StringBuilder sb = new StringBuilder("select distinct t.TAG_ID_C as c0, t.TAG_NAME_C as c1, t.TAG_COLOR_C as c2, t.TAG_IDPARENT_C as c3, u.USE_USERNAME_C as c4 "); + sb.append(" from T_TAG t "); + sb.append(" join T_USER u on t.TAG_IDUSER_C = u.USE_ID_C "); + + // Add search criterias + if (criteria.getId() != null) { + criteriaList.add("t.TAG_ID_C = :id"); + parameterMap.put("id", criteria.getId()); + } + if (criteria.getTargetIdList() != null) { + sb.append(" left join T_ACL a on a.ACL_TARGETID_C in (:targetIdList) and a.ACL_SOURCEID_C = t.TAG_ID_C and a.ACL_PERM_C = 'READ' and a.ACL_DELETEDATE_D is null "); + criteriaList.add("a.ACL_ID_C is not null"); + parameterMap.put("targetIdList", criteria.getTargetIdList()); + } + if (criteria.getDocumentId() != null) { + sb.append(" join T_DOCUMENT_TAG dt on dt.DOT_IDTAG_C = t.TAG_ID_C and dt.DOT_DELETEDATE_D is null "); + criteriaList.add("dt.DOT_IDDOCUMENT_C = :documentId"); + parameterMap.put("documentId", criteria.getDocumentId()); + } + if (criteria.getName() != null) { + criteriaList.add("t.TAG_NAME_C = :name"); + parameterMap.put("name", criteria.getName()); + } + if (criteria.getNameLike() != null) { + criteriaList.add("t.TAG_NAME_C like :nameLike"); + parameterMap.put("nameLike", "%" + criteria.getNameLike() + "%"); + } + + criteriaList.add("t.TAG_DELETEDATE_D is null"); + + if (!criteriaList.isEmpty()) { + sb.append(" where "); + sb.append(Joiner.on(" and ").join(criteriaList)); + } + + // Perform the search + QueryParam queryParam = QueryUtil.getSortedQueryParam(new QueryParam(sb.toString(), parameterMap), sortCriteria); + @SuppressWarnings("unchecked") + List l = QueryUtil.getNativeQuery(queryParam).getResultList(); + + // Assemble results + List tagDtoList = new ArrayList<>(); + for (Object[] o : l) { + int i = 0; + TagDto tagDto = new TagDto() + .setId((String) o[i++]) + .setName((String) o[i++]) + .setColor((String) o[i++]) + .setParentId((String) o[i++]) + .setCreator((String) o[i]); + tagDtoList.add(tagDto); + } + + return tagDtoList; + } } diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java index bc392947..216177cd 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/UserDao.java @@ -188,23 +188,6 @@ public class UserDao { } } - /** - * Gets an active user by its password recovery token. - * - * @param passwordResetKey Password recovery token - * @return User - */ - public User getActiveByPasswordResetKey(String passwordResetKey) { - EntityManager em = ThreadLocalContext.get().getEntityManager(); - try { - Query q = em.createQuery("select u from User u where u.passwordResetKey = :passwordResetKey and u.deleteDate is null"); - q.setParameter("passwordResetKey", passwordResetKey); - return (User) q.getSingleResult(); - } catch (NoResultException e) { - return null; - } - } - /** * Deletes a user. * @@ -258,7 +241,7 @@ public class UserDao { * @param password Clear password * @return Hashed password */ - protected String hashPassword(String password) { + private String hashPassword(String password) { return BCrypt.hashpw(password, BCrypt.gensalt()); } @@ -270,8 +253,8 @@ public class UserDao { * @return List of users */ public List findByCriteria(UserCriteria criteria, SortCriteria sortCriteria) { - Map parameterMap = new HashMap(); - List criteriaList = new ArrayList(); + Map parameterMap = new HashMap<>(); + List criteriaList = new ArrayList<>(); StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5"); sb.append(" from T_USER u "); @@ -300,7 +283,7 @@ public class UserDao { List l = QueryUtil.getNativeQuery(queryParam).getResultList(); // Assemble results - List userDtoList = new ArrayList(); + List userDtoList = new ArrayList<>(); for (Object[] o : l) { int i = 0; UserDto userDto = new UserDto(); @@ -309,7 +292,7 @@ public class UserDao { userDto.setEmail((String) o[i++]); userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime()); userDto.setStorageCurrent(((Number) o[i++]).longValue()); - userDto.setStorageQuota(((Number) o[i++]).longValue()); + userDto.setStorageQuota(((Number) o[i]).longValue()); userDtoList.add(userDto); } return userDtoList; diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/criteria/TagCriteria.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/criteria/TagCriteria.java new file mode 100644 index 00000000..2665a205 --- /dev/null +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/criteria/TagCriteria.java @@ -0,0 +1,80 @@ +package com.sismics.docs.core.dao.jpa.criteria; + +import java.util.List; + +/** + * Tag criteria. + * + * @author bgamard + */ +public class TagCriteria { + /** + * Tag ID. + */ + private String id; + + /** + * ACL target ID list. + */ + private List targetIdList; + + /** + * Document ID. + */ + private String documentId; + + /** + * Tag name. + */ + private String name; + + /** + * Approximate tag name. + */ + private String nameLike; + + public String getId() { + return id; + } + + public TagCriteria setId(String id) { + this.id = id; + return this; + } + + public List getTargetIdList() { + return targetIdList; + } + + public TagCriteria setTargetIdList(List targetIdList) { + this.targetIdList = targetIdList; + return this; + } + + public String getDocumentId() { + return documentId; + } + + public TagCriteria setDocumentId(String documentId) { + this.documentId = documentId; + return this; + } + + public String getName() { + return name; + } + + public TagCriteria setName(String name) { + this.name = name; + return this; + } + + public String getNameLike() { + return nameLike; + } + + public TagCriteria setNameLike(String nameLike) { + this.nameLike = nameLike; + return this; + } +} diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/TagDto.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/TagDto.java index 81bea5a1..de40d7bb 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/TagDto.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/TagDto.java @@ -26,35 +26,53 @@ public class TagDto { */ private String parentId; + /** + * Creator. + */ + private String creator; + public String getId() { return id; } - public void setId(String id) { + public TagDto setId(String id) { this.id = id; + return this; } public String getName() { return name; } - public void setName(String name) { + public TagDto setName(String name) { this.name = name; + return this; } public String getColor() { return color; } - public void setColor(String color) { + public TagDto setColor(String color) { this.color = color; + return this; } public String getParentId() { return parentId; } - public void setParentId(String parentId) { + public TagDto setParentId(String parentId) { this.parentId = parentId; + return this; + } + + public String getCreator() { + return creator; + } + + public TagDto setCreator(String creator) { + this.creator = creator; + return this; } } diff --git a/docs-core/src/main/java/com/sismics/util/jpa/DbOpenHelper.java b/docs-core/src/main/java/com/sismics/util/jpa/DbOpenHelper.java index d742f670..dac8b80c 100644 --- a/docs-core/src/main/java/com/sismics/util/jpa/DbOpenHelper.java +++ b/docs-core/src/main/java/com/sismics/util/jpa/DbOpenHelper.java @@ -37,27 +37,25 @@ import com.sismics.util.ResourceUtil; * * @author jtremeaux */ -public abstract class DbOpenHelper { +abstract class DbOpenHelper { /** * Logger. */ private static final Logger log = LoggerFactory.getLogger(DbOpenHelper.class); - private final SqlStatementLogger sqlStatementLogger; - private final JdbcConnectionAccess jdbcConnectionAccess; - private final List exceptions = new ArrayList(); + private final List exceptions = new ArrayList<>(); private Formatter formatter; private boolean haltOnError; - + private Statement stmt; - public DbOpenHelper(ServiceRegistry serviceRegistry) throws HibernateException { + DbOpenHelper(ServiceRegistry serviceRegistry) throws HibernateException { final JdbcServices jdbcServices = serviceRegistry.getService(JdbcServices.class); - sqlStatementLogger = jdbcServices.getSqlStatementLogger(); + SqlStatementLogger sqlStatementLogger = jdbcServices.getSqlStatementLogger(); jdbcConnectionAccess = jdbcServices.getBootstrapJdbcConnectionAccess(); formatter = (sqlStatementLogger.isFormat() ? FormatStyle.DDL : FormatStyle.NONE).getFormatter(); } @@ -66,7 +64,6 @@ public abstract class DbOpenHelper { log.info("Opening database and executing incremental updates"); Connection connection = null; - Writer outputFileWriter = null; exceptions.clear(); @@ -130,14 +127,6 @@ public abstract class DbOpenHelper { exceptions.add(e); log.error("Unable to close connection", e); } - try { - if (outputFileWriter != null) { - outputFileWriter.close(); - } - } catch (Exception e) { - exceptions.add(e); - log.error("Unable to close connection", e); - } } } @@ -147,7 +136,7 @@ public abstract class DbOpenHelper { * @param version Version number * @throws Exception */ - protected void executeAllScript(final int version) throws Exception { + void executeAllScript(final int version) throws Exception { List fileNameList = ResourceUtil.list(getClass(), "/db/update/", new FilenameFilter() { @Override public boolean accept(File dir, String name) { @@ -173,7 +162,7 @@ public abstract class DbOpenHelper { * @throws IOException * @throws SQLException */ - protected void executeScript(InputStream inputScript) throws IOException, SQLException { + void executeScript(InputStream inputScript) throws IOException, SQLException { List lines = CharStreams.readLines(new InputStreamReader(inputScript)); for (String sql : lines) { diff --git a/docs-core/src/test/java/com/sismics/docs/core/util/TestFileUtil.java b/docs-core/src/test/java/com/sismics/docs/core/util/TestFileUtil.java index bf3bb1de..8ce18dc4 100644 --- a/docs-core/src/test/java/com/sismics/docs/core/util/TestFileUtil.java +++ b/docs-core/src/test/java/com/sismics/docs/core/util/TestFileUtil.java @@ -97,10 +97,9 @@ public class TestFileUtil { File file4 = new File(); file4.setId("document_odt"); file4.setMimeType(MimeType.OPEN_DOCUMENT_TEXT); - - try (InputStream pdfInputStream = PdfUtil.convertToPdf(documentDto, Lists.newArrayList(file0, file1, file2, file3, file4), true, true, 10)) { - ByteStreams.copy(pdfInputStream, System.out); - } + + InputStream is = PdfUtil.convertToPdf(documentDto, Lists.newArrayList(file0, file1, file2, file3, file4), true, true, 10); + is.close(); } } } diff --git a/docs-stress/pom.xml b/docs-stress/pom.xml index ec85c4d0..6c917af7 100644 --- a/docs-stress/pom.xml +++ b/docs-stress/pom.xml @@ -6,7 +6,7 @@ com.sismics.docs docs-parent 1.0-SNAPSHOT - ../docs-parent + .. 4.0.0 diff --git a/docs-web-common/pom.xml b/docs-web-common/pom.xml index 38df767c..427999c7 100644 --- a/docs-web-common/pom.xml +++ b/docs-web-common/pom.xml @@ -1,11 +1,12 @@ - + com.sismics.docs docs-parent 1.0-SNAPSHOT - ../docs-parent + .. 4.0.0 diff --git a/docs-web-common/src/main/java/com/sismics/rest/util/AclUtil.java b/docs-web-common/src/main/java/com/sismics/rest/util/AclUtil.java new file mode 100644 index 00000000..5e4f5865 --- /dev/null +++ b/docs-web-common/src/main/java/com/sismics/rest/util/AclUtil.java @@ -0,0 +1,40 @@ +package com.sismics.rest.util; + +import com.sismics.docs.core.constant.PermType; +import com.sismics.docs.core.dao.jpa.AclDao; +import com.sismics.docs.core.dao.jpa.dto.AclDto; +import com.sismics.security.IPrincipal; + +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; +import java.util.List; + +/** + * Acl utilities. + * + * @author bgamard + */ +public class AclUtil { + /** + * Add ACLs to a JSON response. + * + * @param json JSON + * @param sourceId Source ID + * @param targetIdList List of target ID + */ + public static void addAcls(JsonObjectBuilder json, String sourceId, List targetIdList) { + AclDao aclDao = new AclDao(); + List aclDtoList = aclDao.getBySourceId(sourceId); + JsonArrayBuilder aclList = Json.createArrayBuilder(); + for (AclDto aclDto : aclDtoList) { + aclList.add(Json.createObjectBuilder() + .add("perm", aclDto.getPerm().name()) + .add("id", aclDto.getTargetId()) + .add("name", JsonUtil.nullable(aclDto.getTargetName())) + .add("type", aclDto.getTargetType())); + } + json.add("acls", aclList) + .add("writable", aclDao.checkPermission(sourceId, PermType.WRITE, targetIdList)); + } +} diff --git a/docs-web-common/src/main/java/com/sismics/rest/util/JsonUtil.java b/docs-web-common/src/main/java/com/sismics/rest/util/JsonUtil.java index a244ef1a..039cc063 100644 --- a/docs-web-common/src/main/java/com/sismics/rest/util/JsonUtil.java +++ b/docs-web-common/src/main/java/com/sismics/rest/util/JsonUtil.java @@ -9,7 +9,6 @@ import javax.json.JsonValue; * @author bgamard */ public class JsonUtil { - /** * Returns a JsonValue from a String. * diff --git a/docs-web-common/src/main/java/com/sismics/util/filter/RequestContextFilter.java b/docs-web-common/src/main/java/com/sismics/util/filter/RequestContextFilter.java index f3eff148..6acdef32 100644 --- a/docs-web-common/src/main/java/com/sismics/util/filter/RequestContextFilter.java +++ b/docs-web-common/src/main/java/com/sismics/util/filter/RequestContextFilter.java @@ -80,7 +80,7 @@ public class RequestContextFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { - EntityManager em = null; + EntityManager em; try { em = EMF.get().createEntityManager(); diff --git a/docs-web-common/src/test/java/com/sismics/docs/rest/BaseJerseyTest.java b/docs-web-common/src/test/java/com/sismics/docs/rest/BaseJerseyTest.java index 283a76a6..e4244d6f 100644 --- a/docs-web-common/src/test/java/com/sismics/docs/rest/BaseJerseyTest.java +++ b/docs-web-common/src/test/java/com/sismics/docs/rest/BaseJerseyTest.java @@ -30,7 +30,7 @@ public abstract class BaseJerseyTest extends JerseyTest { /** * Test HTTP server. */ - protected HttpServer httpServer; + private HttpServer httpServer; /** * Utility class for the REST client. @@ -45,7 +45,11 @@ public abstract class BaseJerseyTest extends JerseyTest { @Override protected Application configure() { enable(TestProperties.LOG_TRAFFIC); - enable(TestProperties.DUMP_ENTITY); + String travisEnv = System.getenv("TRAVIS"); + if (travisEnv == null || !travisEnv.equals("true")) { + // Travis don't like entity dumped in the logs + enable(TestProperties.DUMP_ENTITY); + } return new Application(); } diff --git a/docs-web/pom.xml b/docs-web/pom.xml index 048cc060..e4f2420a 100644 --- a/docs-web/pom.xml +++ b/docs-web/pom.xml @@ -6,7 +6,7 @@ com.sismics.docs docs-parent 1.0-SNAPSHOT - ../docs-parent + .. 4.0.0 diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/AclResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/AclResource.java index 71afdf40..480b9a51 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/AclResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/AclResource.java @@ -1,40 +1,27 @@ package com.sismics.docs.rest.resource; -import java.text.MessageFormat; -import java.util.List; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response; - import com.google.common.collect.Lists; import com.sismics.docs.core.constant.AclTargetType; import com.sismics.docs.core.constant.PermType; -import com.sismics.docs.core.dao.jpa.AclDao; -import com.sismics.docs.core.dao.jpa.DocumentDao; -import com.sismics.docs.core.dao.jpa.GroupDao; -import com.sismics.docs.core.dao.jpa.UserDao; +import com.sismics.docs.core.dao.jpa.*; import com.sismics.docs.core.dao.jpa.criteria.GroupCriteria; import com.sismics.docs.core.dao.jpa.criteria.UserCriteria; import com.sismics.docs.core.dao.jpa.dto.GroupDto; import com.sismics.docs.core.dao.jpa.dto.UserDto; -import com.sismics.docs.core.model.jpa.Acl; -import com.sismics.docs.core.model.jpa.Document; -import com.sismics.docs.core.model.jpa.Group; -import com.sismics.docs.core.model.jpa.User; +import com.sismics.docs.core.model.jpa.*; import com.sismics.docs.core.util.jpa.SortCriteria; import com.sismics.rest.exception.ClientException; import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.util.ValidationUtil; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; +import java.text.MessageFormat; +import java.util.List; + /** * ACL REST resources. * @@ -48,7 +35,7 @@ public class AclResource extends BaseResource { * @param sourceId Source ID * @param permStr Permission * @param targetName Target name - * @param type ACL type + * @param typeStr ACL type * @return Response */ @PUT @@ -156,7 +143,14 @@ public class AclResource extends BaseResource { if (document != null && document.getUserId().equals(targetId)) { throw new ClientException("AclError", "Cannot delete base ACL on a document"); } - + + // Cannot delete R/W on a source tag if the target is the creator + TagDao tagDao = new TagDao(); + Tag tag = tagDao.getById(sourceId); + if (tag != null && tag.getUserId().equals(targetId)) { + throw new ClientException("AclError", "Cannot delete base ACL on a tag"); + } + // Delete the ACL aclDao.delete(sourceId, perm, targetId, principal.getId()); diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java index 6aa342ac..80f8c02e 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/AppResource.java @@ -20,6 +20,13 @@ import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; +import com.sismics.docs.core.constant.PermType; +import com.sismics.docs.core.dao.jpa.AclDao; +import com.sismics.docs.core.dao.jpa.TagDao; +import com.sismics.docs.core.dao.jpa.criteria.TagCriteria; +import com.sismics.docs.core.dao.jpa.dto.AclDto; +import com.sismics.docs.core.dao.jpa.dto.TagDto; +import com.sismics.docs.core.model.jpa.Acl; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Appender; import org.apache.log4j.Level; @@ -284,12 +291,12 @@ public class AppResource extends BaseResource { Map userMap = new HashMap<>(); for (File file : fileList) { java.nio.file.Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId()); - User user = null; + User user; if (userMap.containsKey(file.getUserId())) { user = userMap.get(file.getUserId()); } else { user = userDao.getById(file.getUserId()); - user.setStorageCurrent(0l); + user.setStorageCurrent(0L); userMap.put(user.getId(), user); } @@ -312,4 +319,49 @@ public class AppResource extends BaseResource { .add("status", "ok"); return Response.ok().entity(response.build()).build(); } + + /** + * Add base ACLs to tags. + * + * @return Response + */ + @POST + @Path("batch/tag_acls") + public Response batchTagAcls() { + if (!authenticate()) { + throw new ForbiddenClientException(); + } + checkBaseFunction(BaseFunction.ADMIN); + + // Get all tags + TagDao tagDao = new TagDao(); + List tagDtoList = tagDao.findByCriteria(new TagCriteria(), null); + + // Add READ and WRITE ACLs + for (TagDto tagDto : tagDtoList) { + AclDao aclDao = new AclDao(); + List aclDtoList = aclDao.getBySourceId(tagDto.getId()); + + if (aclDtoList.size() == 0) { + // Create read ACL + Acl acl = new Acl(); + acl.setPerm(PermType.READ); + acl.setSourceId(tagDto.getId()); + acl.setTargetId(principal.getId()); + aclDao.create(acl, principal.getId()); + + // Create write ACL + acl = new Acl(); + acl.setPerm(PermType.WRITE); + acl.setSourceId(tagDto.getId()); + acl.setTargetId(principal.getId()); + aclDao.create(acl, principal.getId()); + } + } + + // Always return OK + JsonObjectBuilder response = Json.createObjectBuilder() + .add("status", "ok"); + return Response.ok().entity(response.build()).build(); + } } diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/AuditLogResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/AuditLogResource.java index 2d42e01e..83f5b798 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/AuditLogResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/AuditLogResource.java @@ -1,14 +1,5 @@ package com.sismics.docs.rest.resource; -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - import com.google.common.base.Strings; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.AclDao; @@ -21,6 +12,15 @@ import com.sismics.docs.core.util.jpa.SortCriteria; import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.util.JsonUtil; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; + /** * Audit log REST resources. * @@ -50,7 +50,7 @@ public class AuditLogResource extends BaseResource { // Check ACL on the document AclDao aclDao = new AclDao(); if (!aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(null))) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } criteria.setDocumentId(documentId); } diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/CommentResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/CommentResource.java index 26a1881f..bd032dcd 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/CommentResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/CommentResource.java @@ -1,20 +1,5 @@ package com.sismics.docs.rest.resource; -import java.util.List; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.CommentDao; import com.sismics.docs.core.dao.jpa.DocumentDao; @@ -24,6 +9,13 @@ import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.util.ValidationUtil; import com.sismics.util.ImageUtil; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; +import java.util.List; + /** * Comment REST resource. * @@ -52,7 +44,7 @@ public class CommentResource extends BaseResource { // Read access on doc gives access to write comments DocumentDao documentDao = new DocumentDao(); if (documentDao.getDocument(documentId, PermType.READ, getTargetIdList(null)) == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Create the comment @@ -90,7 +82,7 @@ public class CommentResource extends BaseResource { CommentDao commentDao = new CommentDao(); Comment comment = commentDao.getActiveById(id); if (comment == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // If the current user owns the comment, skip ACL check @@ -98,7 +90,7 @@ public class CommentResource extends BaseResource { // Get the associated document DocumentDao documentDao = new DocumentDao(); if (documentDao.getDocument(comment.getDocumentId(), PermType.WRITE, getTargetIdList(null)) == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } } @@ -126,7 +118,7 @@ public class CommentResource extends BaseResource { // Read access on doc gives access to read comments DocumentDao documentDao = new DocumentDao(); if (documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId)) == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Assemble results diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java index 0f9d21de..4633e34b 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/DocumentResource.java @@ -1,56 +1,14 @@ package com.sismics.docs.rest.resource; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.StreamingOutput; - -import org.joda.time.DateTime; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.DateTimeFormatterBuilder; -import org.joda.time.format.DateTimeParser; - import com.google.common.base.Joiner; import com.google.common.base.Strings; import com.google.common.io.ByteStreams; import com.sismics.docs.core.constant.Constants; import com.sismics.docs.core.constant.PermType; -import com.sismics.docs.core.dao.jpa.AclDao; -import com.sismics.docs.core.dao.jpa.ContributorDao; -import com.sismics.docs.core.dao.jpa.DocumentDao; -import com.sismics.docs.core.dao.jpa.FileDao; -import com.sismics.docs.core.dao.jpa.RelationDao; -import com.sismics.docs.core.dao.jpa.TagDao; -import com.sismics.docs.core.dao.jpa.UserDao; +import com.sismics.docs.core.dao.jpa.*; import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria; -import com.sismics.docs.core.dao.jpa.dto.AclDto; -import com.sismics.docs.core.dao.jpa.dto.ContributorDto; -import com.sismics.docs.core.dao.jpa.dto.DocumentDto; -import com.sismics.docs.core.dao.jpa.dto.RelationDto; -import com.sismics.docs.core.dao.jpa.dto.TagDto; +import com.sismics.docs.core.dao.jpa.criteria.TagCriteria; +import com.sismics.docs.core.dao.jpa.dto.*; import com.sismics.docs.core.event.DocumentCreatedAsyncEvent; import com.sismics.docs.core.event.DocumentDeletedAsyncEvent; import com.sismics.docs.core.event.DocumentUpdatedAsyncEvent; @@ -59,7 +17,6 @@ import com.sismics.docs.core.model.context.AppContext; import com.sismics.docs.core.model.jpa.Acl; import com.sismics.docs.core.model.jpa.Document; import com.sismics.docs.core.model.jpa.File; -import com.sismics.docs.core.model.jpa.Tag; import com.sismics.docs.core.model.jpa.User; import com.sismics.docs.core.util.PdfUtil; import com.sismics.docs.core.util.jpa.PaginatedList; @@ -68,9 +25,27 @@ import com.sismics.docs.core.util.jpa.SortCriteria; import com.sismics.rest.exception.ClientException; import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.exception.ServerException; +import com.sismics.rest.util.AclUtil; import com.sismics.rest.util.JsonUtil; import com.sismics.rest.util.ValidationUtil; import com.sismics.util.mime.MimeType; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.joda.time.format.DateTimeParser; + +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.*; /** * Document REST resources. @@ -83,6 +58,7 @@ public class DocumentResource extends BaseResource { * Returns a document. * * @param documentId Document ID + * @param shareId Share ID * @return Response */ @GET @@ -93,10 +69,9 @@ public class DocumentResource extends BaseResource { authenticate(); DocumentDao documentDao = new DocumentDao(); - AclDao aclDao = new AclDao(); DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId)); if (documentDto == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } JsonObjectBuilder document = Json.createObjectBuilder() @@ -114,7 +89,11 @@ public class DocumentResource extends BaseResource { } else { // Add tags added by the current user on this document TagDao tagDao = new TagDao(); - List tagDtoList = tagDao.getByDocumentId(documentId, principal.getId()); + List tagDtoList = tagDao.findByCriteria( + new TagCriteria() + .setTargetIdList(getTargetIdList(shareId)) + .setDocumentId(documentId), + new SortCriteria(1, true)); JsonArrayBuilder tags = Json.createArrayBuilder(); for (TagDto tagDto : tagDtoList) { tags.add(Json.createObjectBuilder() @@ -135,28 +114,9 @@ public class DocumentResource extends BaseResource { document.add("coverage", JsonUtil.nullable(documentDto.getCoverage())); document.add("rights", JsonUtil.nullable(documentDto.getRights())); document.add("creator", documentDto.getCreator()); - + // Add ACL - List aclDtoList = aclDao.getBySourceId(documentId); - JsonArrayBuilder aclList = Json.createArrayBuilder(); - boolean writable = false; - for (AclDto aclDto : aclDtoList) { - aclList.add(Json.createObjectBuilder() - .add("perm", aclDto.getPerm().name()) - .add("id", aclDto.getTargetId()) - .add("name", JsonUtil.nullable(aclDto.getTargetName())) - .add("type", aclDto.getTargetType())); - - if (!principal.isAnonymous() - && (aclDto.getTargetId().equals(principal.getId()) - || principal.getGroupIdSet().contains(aclDto.getTargetId())) - && aclDto.getPerm() == PermType.WRITE) { - // The document is writable for the current user - writable = true; - } - } - document.add("acls", aclList) - .add("writable", writable); + AclUtil.addAcls(document, documentId, getTargetIdList(shareId)); // Add contributors ContributorDao contributorDao = new ContributorDao(); @@ -188,6 +148,11 @@ public class DocumentResource extends BaseResource { * Export a document to PDF. * * @param documentId Document ID + * @param shareId Share ID + * @param metadata Export metadata + * @param comments Export comments + * @param fitImageToPage Fit images to page + * @param marginStr Margins * @return Response */ @GET @@ -208,7 +173,7 @@ public class DocumentResource extends BaseResource { DocumentDao documentDao = new DocumentDao(); final DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId)); if (documentDto == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Get files @@ -231,7 +196,11 @@ public class DocumentResource extends BaseResource { } catch (Exception e) { throw new IOException(e); } finally { - outputStream.close(); + try { + outputStream.close(); + } catch (IOException e) { + // Ignore + } } } }; @@ -247,6 +216,9 @@ public class DocumentResource extends BaseResource { * * @param limit Page limit * @param offset Page offset + * @param sortColumn Sort column + * @param asc Sorting + * @param search Search query * @return Response */ @GET @@ -278,7 +250,7 @@ public class DocumentResource extends BaseResource { for (DocumentDto documentDto : paginatedList.getResultList()) { // Get tags added by the current user on this document - List tagDtoList = tagDao.getByDocumentId(documentDto.getId(), principal.getId()); + List tagDtoList = tagDao.findByCriteria(new TagCriteria().setTargetIdList(getTargetIdList(null)).setDocumentId(documentDto.getId()), new SortCriteria(1, true)); JsonArrayBuilder tags = Json.createArrayBuilder(); for (TagDto tagDto : tagDtoList) { tags.add(Json.createObjectBuilder() @@ -337,77 +309,87 @@ public class DocumentResource extends BaseResource { query.add(criteria); continue; } - - if (params[0].equals("tag")) { - // New tag criteria - List tagList = tagDao.findByName(principal.getId(), params[1]); - if (documentCriteria.getTagIdList() == null) { - documentCriteria.setTagIdList(new ArrayList()); - } - if (tagList.size() == 0) { - // No tag found, the request must returns nothing - documentCriteria.getTagIdList().add(UUID.randomUUID().toString()); - } - for (Tag tag : tagList) { - documentCriteria.getTagIdList().add(tag.getId()); - } - } else if (params[0].equals("after") || params[0].equals("before")) { - // New date span criteria - try { - DateTime date = formatter.parseDateTime(params[1]); - if (params[0].equals("before")) documentCriteria.setCreateDateMax(date.toDate()); - else documentCriteria.setCreateDateMin(date.toDate()); - } catch (IllegalArgumentException e) { - // Invalid date, returns no documents - if (params[0].equals("before")) documentCriteria.setCreateDateMax(new Date(0)); - else documentCriteria.setCreateDateMin(new Date(Long.MAX_VALUE / 2)); - } - } else if (params[0].equals("at")) { - // New specific date criteria - try { - if (params[1].length() == 10) { - DateTime date = dayFormatter.parseDateTime(params[1]); - documentCriteria.setCreateDateMin(date.toDate()); - documentCriteria.setCreateDateMax(date.plusDays(1).minusSeconds(1).toDate()); - } else if (params[1].length() == 7) { - DateTime date = monthFormatter.parseDateTime(params[1]); - documentCriteria.setCreateDateMin(date.toDate()); - documentCriteria.setCreateDateMax(date.plusMonths(1).minusSeconds(1).toDate()); - } else if (params[1].length() == 4) { - DateTime date = yearFormatter.parseDateTime(params[1]); - documentCriteria.setCreateDateMin(date.toDate()); - documentCriteria.setCreateDateMax(date.plusYears(1).minusSeconds(1).toDate()); + + switch (params[0]) { + case "tag": + // New tag criteria + List tagDtoList = tagDao.findByCriteria(new TagCriteria().setTargetIdList(getTargetIdList(null)).setNameLike(params[1]), null); + if (documentCriteria.getTagIdList() == null) { + documentCriteria.setTagIdList(new ArrayList()); } - } catch (IllegalArgumentException e) { - // Invalid date, returns no documents - documentCriteria.setCreateDateMin(new Date(0)); - documentCriteria.setCreateDateMax(new Date(0)); - } - } else if (params[0].equals("shared")) { - // New shared state criteria - if (params[1].equals("yes")) { - documentCriteria.setShared(true); - } - } else if (params[0].equals("lang")) { - // New language criteria - if (Constants.SUPPORTED_LANGUAGES.contains(params[1])) { - documentCriteria.setLanguage(params[1]); - } - } else if (params[0].equals("by")) { - // New creator criteria - User user = userDao.getActiveByUsername(params[1]); - if (user == null) { - // This user doesn't exists, return nothing - documentCriteria.setCreatorId(UUID.randomUUID().toString()); - } else { - // This user exists, search its documents - documentCriteria.setCreatorId(user.getId()); - } - } else if (params[0].equals("full")) { - // New full content search criteria - fullQuery.add(params[1]); - } else { - query.add(criteria); + if (tagDtoList.size() == 0) { + // No tag found, the request must returns nothing + documentCriteria.getTagIdList().add(UUID.randomUUID().toString()); + } + for (TagDto tagDto : tagDtoList) { + documentCriteria.getTagIdList().add(tagDto.getId()); + } + break; + case "after": + case "before": + // New date span criteria + try { + DateTime date = formatter.parseDateTime(params[1]); + if (params[0].equals("before")) documentCriteria.setCreateDateMax(date.toDate()); + else documentCriteria.setCreateDateMin(date.toDate()); + } catch (IllegalArgumentException e) { + // Invalid date, returns no documents + if (params[0].equals("before")) documentCriteria.setCreateDateMax(new Date(0)); + else documentCriteria.setCreateDateMin(new Date(Long.MAX_VALUE / 2)); + } + break; + case "at": + // New specific date criteria + try { + if (params[1].length() == 10) { + DateTime date = dayFormatter.parseDateTime(params[1]); + documentCriteria.setCreateDateMin(date.toDate()); + documentCriteria.setCreateDateMax(date.plusDays(1).minusSeconds(1).toDate()); + } else if (params[1].length() == 7) { + DateTime date = monthFormatter.parseDateTime(params[1]); + documentCriteria.setCreateDateMin(date.toDate()); + documentCriteria.setCreateDateMax(date.plusMonths(1).minusSeconds(1).toDate()); + } else if (params[1].length() == 4) { + DateTime date = yearFormatter.parseDateTime(params[1]); + documentCriteria.setCreateDateMin(date.toDate()); + documentCriteria.setCreateDateMax(date.plusYears(1).minusSeconds(1).toDate()); + } + } catch (IllegalArgumentException e) { + // Invalid date, returns no documents + documentCriteria.setCreateDateMin(new Date(0)); + documentCriteria.setCreateDateMax(new Date(0)); + } + break; + case "shared": + // New shared state criteria + if (params[1].equals("yes")) { + documentCriteria.setShared(true); + } + break; + case "lang": + // New language criteria + if (Constants.SUPPORTED_LANGUAGES.contains(params[1])) { + documentCriteria.setLanguage(params[1]); + } + break; + case "by": + // New creator criteria + User user = userDao.getActiveByUsername(params[1]); + if (user == null) { + // This user doesn't exists, return nothing + documentCriteria.setCreatorId(UUID.randomUUID().toString()); + } else { + // This user exists, search its documents + documentCriteria.setCreatorId(user.getId()); + } + break; + case "full": + // New full content search criteria + fullQuery.add(params[1]); + break; + default: + query.add(criteria); + break; } } @@ -421,7 +403,16 @@ public class DocumentResource extends BaseResource { * * @param title Title * @param description Description - * @param tags Tags + * @param subject Subject + * @param identifier Identifier + * @param publisher Publisher + * @param format Format + * @param source Source + * @param type Type + * @param coverage Coverage + * @param rights Rights + * @param tagList Tags + * @param relationList Relations * @param language Language * @param createDateStr Creation date * @return Response @@ -573,7 +564,7 @@ public class DocumentResource extends BaseResource { DocumentDao documentDao = new DocumentDao(); Document document = documentDao.getById(id); if (document == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Update the document @@ -594,7 +585,7 @@ public class DocumentResource extends BaseResource { document.setCreateDate(createDate); } - document = documentDao.update(document, principal.getId()); + documentDao.update(document, principal.getId()); // Update tags updateTagList(id, tagList); @@ -624,9 +615,9 @@ public class DocumentResource extends BaseResource { TagDao tagDao = new TagDao(); Set tagSet = new HashSet<>(); Set tagIdSet = new HashSet<>(); - List tagDbList = tagDao.getByUserId(principal.getId()); - for (Tag tagDb : tagDbList) { - tagIdSet.add(tagDb.getId()); + List tagDtoList = tagDao.findByCriteria(new TagCriteria().setTargetIdList(getTargetIdList(null)), null); + for (TagDto tagDto : tagDtoList) { + tagIdSet.add(tagDto.getId()); } for (String tagId : tagList) { if (!tagIdSet.contains(tagId)) { @@ -679,7 +670,7 @@ public class DocumentResource extends BaseResource { FileDao fileDao = new FileDao(); DocumentDto documentDto = documentDao.getDocument(id, PermType.WRITE, getTargetIdList(null)); if (documentDto == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } List fileList = fileDao.getByDocumentId(principal.getId(), id); diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java index 084eb13b..31a3af3a 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/FileResource.java @@ -16,17 +16,7 @@ import java.util.zip.ZipOutputStream; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObjectBuilder; -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; +import javax.ws.rs.*; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; @@ -100,7 +90,7 @@ public class FileResource extends BaseResource { DocumentDao documentDao = new DocumentDao(); documentDto = documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null)); if (documentDto == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } } @@ -215,7 +205,7 @@ public class FileResource extends BaseResource { File file = fileDao.getFile(id, principal.getId()); DocumentDto documentDto = documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null)); if (file == null || documentDto == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Check that the file is orphan @@ -277,7 +267,7 @@ public class FileResource extends BaseResource { // Get the document DocumentDao documentDao = new DocumentDao(); if (documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null)) == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Reorder files @@ -313,7 +303,7 @@ public class FileResource extends BaseResource { if (documentId != null) { AclDao aclDao = new AclDao(); if (!aclDao.checkPermission(documentId, PermType.READ, getTargetIdList(shareId))) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } } else if (!authenticated) { throw new ForbiddenClientException(); @@ -360,7 +350,7 @@ public class FileResource extends BaseResource { DocumentDao documentDao = new DocumentDao(); File file = fileDao.getFile(id); if (file == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } DocumentDto documentDto = null; @@ -371,7 +361,7 @@ public class FileResource extends BaseResource { throw new ForbiddenClientException(); } } else if ((documentDto = documentDao.getDocument(file.getDocumentId(), PermType.WRITE, getTargetIdList(null))) == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Delete the file @@ -433,7 +423,7 @@ public class FileResource extends BaseResource { UserDao userDao = new UserDao(); File file = fileDao.getFile(fileId); if (file == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } if (file.getDocumentId() == null) { @@ -454,7 +444,7 @@ public class FileResource extends BaseResource { // Get the stored file java.nio.file.Path storedFile; String mimeType; - boolean decrypt = false; + boolean decrypt; if (size != null) { storedFile = DirectoryUtil.getStorageDirectory().resolve(fileId + "_" + size); mimeType = MimeType.IMAGE_JPEG; // Thumbnails are JPEG @@ -488,8 +478,12 @@ public class FileResource extends BaseResource { try { ByteStreams.copy(responseInputStream, outputStream); } finally { - responseInputStream.close(); - outputStream.close(); + try { + responseInputStream.close(); + outputStream.close(); + } catch (IOException e) { + // Ignore + } } } }; @@ -521,7 +515,7 @@ public class FileResource extends BaseResource { DocumentDao documentDao = new DocumentDao(); DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId)); if (documentDto == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Get files and user associated with this document diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/GroupResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/GroupResource.java index 8cf8c07c..d345ee97 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/GroupResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/GroupResource.java @@ -1,22 +1,5 @@ package com.sismics.docs.rest.resource; -import java.text.MessageFormat; -import java.util.List; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - import com.google.common.base.Strings; import com.sismics.docs.core.dao.jpa.GroupDao; import com.sismics.docs.core.dao.jpa.UserDao; @@ -34,6 +17,14 @@ import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.util.JsonUtil; import com.sismics.rest.util.ValidationUtil; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; +import java.text.MessageFormat; +import java.util.List; + /** * Group REST resources. * @@ -109,12 +100,12 @@ public class GroupResource extends BaseResource { GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Avoid duplicates Group existingGroup = groupDao.getActiveByName(name); - if (existingGroup != null && existingGroup.getId() != group.getId()) { + if (existingGroup != null && !existingGroup.getId().equals(group.getId())) { throw new ClientException("GroupAlreadyExists", MessageFormat.format("This group already exists: {0}", name)); } @@ -155,7 +146,7 @@ public class GroupResource extends BaseResource { GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Delete the group @@ -191,14 +182,14 @@ public class GroupResource extends BaseResource { GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Get the user UserDao userDao = new UserDao(); User user = userDao.getActiveByUsername(username); if (user == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Avoid duplicates @@ -248,14 +239,14 @@ public class GroupResource extends BaseResource { GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Get the user UserDao userDao = new UserDao(); User user = userDao.getActiveByUsername(username); if (user == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Remove the membership @@ -315,7 +306,7 @@ public class GroupResource extends BaseResource { GroupDao groupDao = new GroupDao(); Group group = groupDao.getActiveByName(groupName); if (group == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Build the response diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/ShareResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/ShareResource.java index 93957469..f51d2214 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/ShareResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/ShareResource.java @@ -1,19 +1,6 @@ package com.sismics.docs.rest.resource; -import java.text.MessageFormat; -import java.util.List; - -import javax.json.Json; -import javax.json.JsonObjectBuilder; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - import com.sismics.docs.core.constant.AclTargetType; import com.sismics.docs.core.constant.PermType; import com.sismics.docs.core.dao.jpa.AclDao; @@ -26,6 +13,13 @@ import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.util.JsonUtil; import com.sismics.rest.util.ValidationUtil; +import javax.json.Json; +import javax.json.JsonObjectBuilder; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; +import java.text.MessageFormat; +import java.util.List; + /** * Share REST resources. * @@ -55,7 +49,7 @@ public class ShareResource extends BaseResource { // Get the document DocumentDao documentDao = new DocumentDao(); if (documentDao.getDocument(documentId, PermType.WRITE, getTargetIdList(null)) == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Create the share diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java index 97b8e4d4..9448f8cf 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java @@ -1,29 +1,28 @@ package com.sismics.docs.rest.resource; -import java.text.MessageFormat; -import java.util.List; +import com.google.common.collect.Sets; +import com.sismics.docs.core.constant.PermType; +import com.sismics.docs.core.dao.jpa.AclDao; +import com.sismics.docs.core.dao.jpa.TagDao; +import com.sismics.docs.core.dao.jpa.criteria.TagCriteria; +import com.sismics.docs.core.dao.jpa.dto.TagDto; +import com.sismics.docs.core.model.jpa.Acl; +import com.sismics.docs.core.model.jpa.Tag; +import com.sismics.docs.core.util.jpa.SortCriteria; +import com.sismics.rest.exception.ClientException; +import com.sismics.rest.exception.ForbiddenClientException; +import com.sismics.rest.util.AclUtil; +import com.sismics.rest.util.ValidationUtil; +import org.apache.commons.lang.StringUtils; import javax.json.Json; import javax.json.JsonArrayBuilder; import javax.json.JsonObjectBuilder; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; +import javax.ws.rs.*; import javax.ws.rs.core.Response; - -import org.apache.commons.lang.StringUtils; - -import com.sismics.docs.core.dao.jpa.TagDao; -import com.sismics.docs.core.dao.jpa.dto.TagStatDto; -import com.sismics.docs.core.model.jpa.Tag; -import com.sismics.rest.exception.ClientException; -import com.sismics.rest.exception.ForbiddenClientException; -import com.sismics.rest.util.JsonUtil; -import com.sismics.rest.util.ValidationUtil; +import java.text.MessageFormat; +import java.util.List; +import java.util.Set; /** * Tag REST resources. @@ -33,7 +32,7 @@ import com.sismics.rest.util.ValidationUtil; @Path("/tag") public class TagResource extends BaseResource { /** - * Returns the list of all tags. + * Returns the list of all visible tags. * * @return Response */ @@ -45,54 +44,71 @@ public class TagResource extends BaseResource { } TagDao tagDao = new TagDao(); - List tagList = tagDao.getByUserId(principal.getId()); + List tagDtoList = tagDao.findByCriteria(new TagCriteria().setTargetIdList(getTargetIdList(null)), new SortCriteria(1, true)); + + // Extract tag IDs + Set tagIdSet = Sets.newHashSet(); + for (TagDto tagDto : tagDtoList) { + tagIdSet.add(tagDto.getId()); + } + + // Build the response JsonArrayBuilder items = Json.createArrayBuilder(); - for (Tag tag : tagList) { - items.add(Json.createObjectBuilder() - .add("id", tag.getId()) - .add("name", tag.getName()) - .add("color", tag.getColor()) - .add("parent", JsonUtil.nullable(tag.getParentId()))); + for (TagDto tagDto : tagDtoList) { + JsonObjectBuilder item = Json.createObjectBuilder() + .add("id", tagDto.getId()) + .add("name", tagDto.getName()) + .add("color", tagDto.getColor()); + if (tagIdSet.contains(tagDto.getParentId())) { + item.add("parent", tagDto.getParentId()); + } + items.add(item); } JsonObjectBuilder response = Json.createObjectBuilder() .add("tags", items); return Response.ok().entity(response.build()).build(); } - + /** - * Returns stats on tags. - * + * Returns a tag. + * + * @param id Tag ID * @return Response */ @GET - @Path("/stats") - public Response stats() { + @Path("{id: [a-z0-9\\-]+}") + public Response get(@PathParam("id") String id) { if (!authenticate()) { throw new ForbiddenClientException(); } - + TagDao tagDao = new TagDao(); - List tagStatDtoList = tagDao.getStats(principal.getId()); - JsonArrayBuilder items = Json.createArrayBuilder(); - for (TagStatDto tagStatDto : tagStatDtoList) { - items.add(Json.createObjectBuilder() - .add("id", tagStatDto.getId()) - .add("name", tagStatDto.getName()) - .add("color", tagStatDto.getColor()) - .add("parent", JsonUtil.nullable(tagStatDto.getParentId())) - .add("count", tagStatDto.getCount())); + List tagDtoList = tagDao.findByCriteria(new TagCriteria().setTargetIdList(getTargetIdList(null)).setId(id), null); + if (tagDtoList.size() == 0) { + throw new NotFoundException(); } - - JsonObjectBuilder response = Json.createObjectBuilder() - .add("stats", items); - return Response.ok().entity(response.build()).build(); + + // Add tag informatiosn + TagDto tagDto = tagDtoList.get(0); + JsonObjectBuilder tag = Json.createObjectBuilder() + .add("id", tagDto.getId()) + .add("creator", tagDto.getCreator()) + .add("name", tagDto.getName()) + .add("color", tagDto.getColor()); + + // Add ACL + AclUtil.addAcls(tag, id, getTargetIdList(null)); + + return Response.ok().entity(tag.build()).build(); } - + /** * Creates a new tag. * * @param name Name + * @param color Color + * @param parentId Parent ID * @return Response */ @PUT @@ -113,30 +129,39 @@ public class TagResource extends BaseResource { throw new ClientException("SpacesNotAllowed", "Spaces are not allowed in tag name"); } - // Get the tag - TagDao tagDao = new TagDao(); - Tag tag = tagDao.getByName(principal.getId(), name); - if (tag != null) { - throw new ClientException("AlreadyExistingTag", MessageFormat.format("Tag already exists: {0}", name)); - } - // Check the parent if (StringUtils.isEmpty(parentId)) { parentId = null; } else { - Tag parentTag = tagDao.getByTagId(principal.getId(), parentId); - if (parentTag == null) { + AclDao aclDao = new AclDao(); + if (!aclDao.checkPermission(parentId, PermType.READ, getTargetIdList(null))) { throw new ClientException("ParentNotFound", MessageFormat.format("Parent not found: {0}", parentId)); } } // Create the tag - tag = new Tag(); + TagDao tagDao = new TagDao(); + Tag tag = new Tag(); tag.setName(name); tag.setColor(color); tag.setUserId(principal.getId()); tag.setParentId(parentId); String id = tagDao.create(tag, principal.getId()); + + // Create read ACL + AclDao aclDao = new AclDao(); + Acl acl = new Acl(); + acl.setPerm(PermType.READ); + acl.setSourceId(id); + acl.setTargetId(principal.getId()); + aclDao.create(acl, principal.getId()); + + // Create write ACL + acl = new Acl(); + acl.setPerm(PermType.WRITE); + acl.setSourceId(id); + acl.setTargetId(principal.getId()); + aclDao.create(acl, principal.getId()); JsonObjectBuilder response = Json.createObjectBuilder() .add("id", id); @@ -147,6 +172,8 @@ public class TagResource extends BaseResource { * Update a tag. * * @param name Name + * @param color Color + * @param parentId Parent ID * @return Response */ @POST @@ -169,30 +196,24 @@ public class TagResource extends BaseResource { throw new ClientException("SpacesNotAllowed", "Spaces are not allowed in tag name"); } - // Get the tag - TagDao tagDao = new TagDao(); - Tag tag = tagDao.getByTagId(principal.getId(), id); - if (tag == null) { - throw new ClientException("TagNotFound", MessageFormat.format("Tag not found: {0}", id)); + // Check permission + AclDao aclDao = new AclDao(); + if (!aclDao.checkPermission(id, PermType.WRITE, getTargetIdList(null))) { + throw new NotFoundException(); } // Check the parent if (StringUtils.isEmpty(parentId)) { parentId = null; } else { - Tag parentTag = tagDao.getByTagId(principal.getId(), parentId); - if (parentTag == null) { + if (!aclDao.checkPermission(parentId, PermType.READ, getTargetIdList(null))) { throw new ClientException("ParentNotFound", MessageFormat.format("Parent not found: {0}", parentId)); } } - // Check for name duplicate - Tag tagDuplicate = tagDao.getByName(principal.getId(), name); - if (tagDuplicate != null && !tagDuplicate.getId().equals(id)) { - throw new ClientException("AlreadyExistingTag", MessageFormat.format("Tag already exists: {0}", name)); - } - // Update the tag + TagDao tagDao = new TagDao(); + Tag tag = tagDao.getById(id); if (!StringUtils.isEmpty(name)) { tag.setName(name); } @@ -212,26 +233,26 @@ public class TagResource extends BaseResource { /** * Delete a tag. * - * @param tagId Tag ID + * @param id Tag ID * @return Response */ @DELETE @Path("{id: [a-z0-9\\-]+}") public Response delete( - @PathParam("id") String tagId) { + @PathParam("id") String id) { if (!authenticate()) { throw new ForbiddenClientException(); } // Get the tag - TagDao tagDao = new TagDao(); - Tag tag = tagDao.getByTagId(principal.getId(), tagId); - if (tag == null) { - throw new ClientException("TagNotFound", MessageFormat.format("Tag not found: {0}", tagId)); + AclDao aclDao = new AclDao(); + if (!aclDao.checkPermission(id, PermType.WRITE, getTargetIdList(null))) { + throw new NotFoundException(); } // Delete the tag - tagDao.delete(tagId, principal.getId()); + TagDao tagDao = new TagDao(); + tagDao.delete(id, principal.getId()); // Always return OK JsonObjectBuilder response = Json.createObjectBuilder() diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java index 1c12c419..433812ec 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/UserResource.java @@ -99,7 +99,7 @@ public class UserResource extends BaseResource { user.setPassword(password); user.setEmail(email); user.setStorageQuota(storageQuota); - user.setStorageCurrent(0l); + user.setStorageCurrent(0L); try { user.setPrivateKey(EncryptionUtil.generatePrivateKey()); } catch (NoSuchAlgorithmException e) { @@ -678,7 +678,7 @@ public class UserResource extends BaseResource { UserDao userDao = new UserDao(); User user = userDao.getActiveByUsername(principal.getName()); user.setTotpKey(key.getKey()); - user = userDao.update(user, principal.getId()); + userDao.update(user, principal.getId()); JsonObjectBuilder response = Json.createObjectBuilder() .add("secret", key.getKey()); diff --git a/docs-web/src/main/java/com/sismics/docs/rest/resource/VocabularyResource.java b/docs-web/src/main/java/com/sismics/docs/rest/resource/VocabularyResource.java index 479d86d2..06ba5ee8 100644 --- a/docs-web/src/main/java/com/sismics/docs/rest/resource/VocabularyResource.java +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/VocabularyResource.java @@ -1,26 +1,18 @@ package com.sismics.docs.rest.resource; -import java.util.List; - -import javax.json.Json; -import javax.json.JsonArrayBuilder; -import javax.json.JsonObjectBuilder; -import javax.ws.rs.DELETE; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - import com.sismics.docs.core.dao.jpa.VocabularyDao; import com.sismics.docs.core.model.jpa.Vocabulary; import com.sismics.docs.rest.constant.BaseFunction; import com.sismics.rest.exception.ForbiddenClientException; import com.sismics.rest.util.ValidationUtil; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; +import javax.ws.rs.*; +import javax.ws.rs.core.Response; +import java.util.List; + /** * Vocabulary REST resources. * @@ -58,7 +50,7 @@ public class VocabularyResource extends BaseResource { * * @param name Name * @param value Value - * @param order Order + * @param orderStr Order * @return Response */ @PUT @@ -95,10 +87,11 @@ public class VocabularyResource extends BaseResource { /** * Update a vocabulary. - * + * + * @param id ID * @param name Name * @param value Value - * @param order Order + * @param orderStr Order * @return Response */ @POST @@ -127,7 +120,7 @@ public class VocabularyResource extends BaseResource { VocabularyDao vocabularyDao = new VocabularyDao(); Vocabulary vocabulary = vocabularyDao.getById(id); if (vocabulary == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Update the vocabulary @@ -169,7 +162,7 @@ public class VocabularyResource extends BaseResource { VocabularyDao vocabularyDao = new VocabularyDao(); Vocabulary vocabulary = vocabularyDao.getById(id); if (vocabulary == null) { - return Response.status(Status.NOT_FOUND).build(); + throw new NotFoundException(); } // Delete the vocabulary diff --git a/docs-web/src/main/webapp/src/app/docs/app.js b/docs-web/src/main/webapp/src/app/docs/app.js index 62e6976a..8c9d4fca 100644 --- a/docs-web/src/main/webapp/src/app/docs/app.js +++ b/docs-web/src/main/webapp/src/app/docs/app.js @@ -13,36 +13,55 @@ angular.module('docs', * Configuring modules. */ .config(function($stateProvider, $httpProvider, RestangularProvider) { + // Configuring UI Router $stateProvider - .state('main', { - url: '', - views: { - 'page': { - templateUrl: 'partial/docs/main.html', - controller: 'Main' + .state('main', { + url: '', + views: { + 'page': { + templateUrl: 'partial/docs/main.html', + controller: 'Main' + } } - } - }) - .state('tag', { - url: '/tag', - views: { - 'page': { - templateUrl: 'partial/docs/tag.html', - controller: 'Tag' + }) + .state('tag', { + url: '/tag', + abstract: true, + views: { + 'page': { + templateUrl: 'partial/docs/tag.html', + controller: 'Tag' + } } - } - }) - .state('settings', { - url: '/settings', - abstract: true, - views: { - 'page': { - templateUrl: 'partial/docs/settings.html', - controller: 'Settings' + }) + .state('tag.default', { + url: '', + views: { + 'tag': { + templateUrl: 'partial/docs/tag.default.html' + } } - } - }) + }) + .state('tag.edit', { + url: '/:id', + views: { + 'tag': { + templateUrl: 'partial/docs/tag.edit.html', + controller: 'TagEdit' + } + } + }) + .state('settings', { + url: '/settings', + abstract: true, + views: { + 'page': { + templateUrl: 'partial/docs/settings.html', + controller: 'Settings' + } + } + }) .state('settings.default', { url: '', views: { @@ -106,70 +125,70 @@ angular.module('docs', } } }) - .state('settings.user.edit', { - url: '/edit/:username', - views: { - 'user': { - templateUrl: 'partial/docs/settings.user.edit.html', - controller: 'SettingsUserEdit' - } + .state('settings.user.edit', { + url: '/edit/:username', + views: { + 'user': { + templateUrl: 'partial/docs/settings.user.edit.html', + controller: 'SettingsUserEdit' } - }) - .state('settings.user.add', { - url: '/add', - views: { - 'user': { - templateUrl: 'partial/docs/settings.user.edit.html', - controller: 'SettingsUserEdit' - } + } + }) + .state('settings.user.add', { + url: '/add', + views: { + 'user': { + templateUrl: 'partial/docs/settings.user.edit.html', + controller: 'SettingsUserEdit' } - }) + } + }) .state('settings.group', { - url: '/group', - views: { - 'settings': { - templateUrl: 'partial/docs/settings.group.html', - controller: 'SettingsGroup' - } + url: '/group', + views: { + 'settings': { + templateUrl: 'partial/docs/settings.group.html', + controller: 'SettingsGroup' } - }) - .state('settings.group.edit', { - url: '/edit/:name', - views: { - 'group': { - templateUrl: 'partial/docs/settings.group.edit.html', - controller: 'SettingsGroupEdit' - } - } - }) - .state('settings.group.add', { - url: '/add', - views: { - 'group': { - templateUrl: 'partial/docs/settings.group.edit.html', - controller: 'SettingsGroupEdit' - } - } - }) - .state('settings.vocabulary', { - url: '/vocabulary', - views: { - 'settings': { - templateUrl: 'partial/docs/settings.vocabulary.html', - controller: 'SettingsVocabulary' } - } - }) - .state('document', { - url: '/document', - abstract: true, - views: { - 'page': { - templateUrl: 'partial/docs/document.html', - controller: 'Document' + }) + .state('settings.group.edit', { + url: '/edit/:name', + views: { + 'group': { + templateUrl: 'partial/docs/settings.group.edit.html', + controller: 'SettingsGroupEdit' + } } - } - }) + }) + .state('settings.group.add', { + url: '/add', + views: { + 'group': { + templateUrl: 'partial/docs/settings.group.edit.html', + controller: 'SettingsGroupEdit' + } + } + }) + .state('settings.vocabulary', { + url: '/vocabulary', + views: { + 'settings': { + templateUrl: 'partial/docs/settings.vocabulary.html', + controller: 'SettingsVocabulary' + } + } + }) + .state('document', { + url: '/document', + abstract: true, + views: { + 'page': { + templateUrl: 'partial/docs/document.html', + controller: 'Document' + } + } + }) .state('document.default', { url: '', views: { @@ -179,17 +198,17 @@ angular.module('docs', } } }) - .state('document.default.search', { - url: '/search/:search' - }) - .state('document.default.file', { - url: '/file/:fileId', - views: { - 'file': { - controller: 'FileView' - } + .state('document.default.search', { + url: '/search/:search' + }) + .state('document.default.file', { + url: '/file/:fileId', + views: { + 'file': { + controller: 'FileView' } - }) + } + }) .state('document.add', { url: '/add?files', views: { @@ -218,59 +237,68 @@ angular.module('docs', } } }) - .state('document.view.content', { - url: '/content', - views: { - 'tab': { - templateUrl: 'partial/docs/document.view.content.html', - controller: 'DocumentViewContent' - } + .state('document.view.content', { + url: '/content', + views: { + 'tab': { + templateUrl: 'partial/docs/document.view.content.html', + controller: 'DocumentViewContent' } - }) - .state('document.view.content.file', { - url: '/file/:fileId', - views: { - 'file': { - controller: 'FileView' - } - } - }) - .state('document.view.permissions', { - url: '/permissions', - views: { - 'tab': { - templateUrl: 'partial/docs/document.view.permissions.html', - controller: 'DocumentViewPermissions' - } - } - }) - .state('document.view.activity', { - url: '/activity', - views: { - 'tab': { - templateUrl: 'partial/docs/document.view.activity.html', - controller: 'DocumentViewActivity' - } - } - }) - .state('login', { - url: '/login', - views: { - 'page': { - templateUrl: 'partial/docs/login.html', - controller: 'Login' } - } - }) - .state('user', { - url: '/user', - views: { - 'page': { - templateUrl: 'partial/docs/usergroup.html', - controller: 'UserGroup' + }) + .state('document.view.content.file', { + url: '/file/:fileId', + views: { + 'file': { + controller: 'FileView' + } } - } - }) + }) + .state('document.view.permissions', { + url: '/permissions', + views: { + 'tab': { + templateUrl: 'partial/docs/document.view.permissions.html', + controller: 'DocumentViewPermissions' + } + } + }) + .state('document.view.activity', { + url: '/activity', + views: { + 'tab': { + templateUrl: 'partial/docs/document.view.activity.html', + controller: 'DocumentViewActivity' + } + } + }) + .state('login', { + url: '/login', + views: { + 'page': { + templateUrl: 'partial/docs/login.html', + controller: 'Login' + } + } + }) + .state('user', { + url: '/user', + abstract: true, + views: { + 'page': { + templateUrl: 'partial/docs/usergroup.html', + controller: 'UserGroup' + } + } + }) + .state('user.default', { + url: '', + views: { + 'sub': { + templateUrl: 'partial/docs/usergroup.default.html' + } + } + }) .state('user.profile', { url: '/:username', views: { @@ -280,15 +308,24 @@ angular.module('docs', } } }) - .state('group', { - url: '/group', - views: { - 'page': { - templateUrl: 'partial/docs/usergroup.html', - controller: 'UserGroup' + .state('group', { + url: '/group', + abstract: true, + views: { + 'page': { + templateUrl: 'partial/docs/usergroup.html', + controller: 'UserGroup' + } } - } - }) + }) + .state('group.default', { + url: '', + views: { + 'sub': { + templateUrl: 'partial/docs/usergroup.default.html' + } + } + }) .state('group.profile', { url: '/:name', views: { @@ -298,7 +335,6 @@ angular.module('docs', } } }); - // Configuring Restangular RestangularProvider.setBaseUrl('../api'); diff --git a/docs-web/src/main/webapp/src/app/docs/controller/document/DocumentViewPermissions.js b/docs-web/src/main/webapp/src/app/docs/controller/document/DocumentViewPermissions.js index 80231f2f..bfbc5493 100644 --- a/docs-web/src/main/webapp/src/app/docs/controller/document/DocumentViewPermissions.js +++ b/docs-web/src/main/webapp/src/app/docs/controller/document/DocumentViewPermissions.js @@ -3,95 +3,5 @@ /** * Document view permissions controller. */ -angular.module('docs').controller('DocumentViewPermissions', function ($scope, $stateParams, Restangular, $q) { - // Watch for ACLs change and group them for easy displaying - $scope.$watch('document.acls', function(acls) { - $scope.acls = _.groupBy(acls, function(acl) { - return acl.id; - }); - }); - - // Initialize add ACL - $scope.acl = { perm: 'READ' }; - - /** - * Delete an ACL. - */ - $scope.deleteAcl = function(acl) { - Restangular.one('acl/' + $stateParams.id + '/' + acl.perm + '/' + acl.id, null).remove().then(function () { - $scope.document.acls = _.reject($scope.document.acls, function(s) { - return angular.equals(acl, s); - }); - }); - }; - - /** - * Add an ACL. - */ - $scope.addAcl = function() { - // Compute ACLs to add - $scope.acl.source = $stateParams.id; - var acls = []; - if ($scope.acl.perm == 'READWRITE') { - acls = [{ - source: $stateParams.id, - target: $scope.acl.target.name, - perm: 'READ', - type: $scope.acl.target.type - }, { - source: $stateParams.id, - target: $scope.acl.target.name, - perm: 'WRITE', - type: $scope.acl.target.type - }]; - } else { - acls = [{ - source: $stateParams.id, - target: $scope.acl.target.name, - perm: $scope.acl.perm, - type: $scope.acl.target.type - }]; - } - - // Add ACLs - _.each(acls, function(acl) { - Restangular.one('acl').put(acl).then(function(acl) { - if (_.isUndefined(acl.id)) { - return; - } - $scope.document.acls.push(acl); - $scope.document.acls = angular.copy($scope.document.acls); - }); - }); - - // Reset form - $scope.acl = { perm: 'READ' }; - }; - - /** - * Auto-complete on ACL target. - */ - $scope.getTargetAclTypeahead = function($viewValue) { - var deferred = $q.defer(); - Restangular.one('acl/target/search') - .get({ - search: $viewValue - }).then(function(data) { - var output = []; - - // Add the type to use later - output.push.apply(output, _.map(data.users, function(user) { - user.type = 'USER'; - return user; - })); - output.push.apply(output, _.map(data.groups, function(group) { - group.type = 'GROUP'; - return group; - })); - - // Send the data to the typeahead directive - deferred.resolve(output, true); - }); - return deferred.promise; - }; +angular.module('docs').controller('DocumentViewPermissions', function() { }); \ No newline at end of file diff --git a/docs-web/src/main/webapp/src/app/docs/controller/tag/Tag.js b/docs-web/src/main/webapp/src/app/docs/controller/tag/Tag.js index 53b44ea1..acf3b5b5 100644 --- a/docs-web/src/main/webapp/src/app/docs/controller/tag/Tag.js +++ b/docs-web/src/main/webapp/src/app/docs/controller/tag/Tag.js @@ -3,37 +3,14 @@ /** * Tag controller. */ -angular.module('docs').controller('Tag', function($scope, $dialog, Tag, Restangular) { +angular.module('docs').controller('Tag', function($scope, $dialog, Restangular) { $scope.tag = { name: '', color: '#3a87ad' }; // Retrieve tags - Tag.tags().then(function(data) { + Restangular.one('tag/list').get().then(function(data) { $scope.tags = data.tags; }); - // Retrieve tag stats - Restangular.one('tag/stats').get().then(function(data) { - $scope.stats = data.stats; - }); - - /** - * Returns total number of document from tag stats. - */ - $scope.getStatCount = function() { - return _.reduce($scope.stats, function(memo, stat) { - return memo + stat.count - }, 0); - }; - - /** - * Validate a tag name for duplicate. - */ - $scope.validateDuplicate = function(name) { - return !_.find($scope.tags, function(tag) { - return tag.name == name; - }); - }; - /** * Add a tag. */ @@ -71,15 +48,6 @@ angular.module('docs').controller('Tag', function($scope, $dialog, Tag, Restangu */ $scope.updateTag = function(tag) { // Update the server - return Restangular.one('tag', tag.id).post('', tag).then(function () { - // Update the stat object - var stat = _.find($scope.stats, function (t) { - return tag.id == t.id; - }); - - if (stat) { - _.extend(stat, tag); - } - }); + return Restangular.one('tag', tag.id).post('', tag); }; }); \ No newline at end of file diff --git a/docs-web/src/main/webapp/src/app/docs/controller/tag/TagEdit.js b/docs-web/src/main/webapp/src/app/docs/controller/tag/TagEdit.js new file mode 100644 index 00000000..1910309d --- /dev/null +++ b/docs-web/src/main/webapp/src/app/docs/controller/tag/TagEdit.js @@ -0,0 +1,10 @@ +'use strict'; + +/** + * Tag edit controller. + */ +angular.module('docs').controller('TagEdit', function($scope, $stateParams, Restangular) { + Restangular.one('tag', $stateParams.id).get().then(function(data) { + $scope.tag = data; + }) +}); \ No newline at end of file diff --git a/docs-web/src/main/webapp/src/app/docs/directive/AclEdit.js b/docs-web/src/main/webapp/src/app/docs/directive/AclEdit.js new file mode 100644 index 00000000..0e53b207 --- /dev/null +++ b/docs-web/src/main/webapp/src/app/docs/directive/AclEdit.js @@ -0,0 +1,112 @@ +'use strict'; + +/** + * ACL edit directive. + */ +angular.module('docs').directive('aclEdit', function() { + return { + restrict: 'E', + templateUrl: 'partial/docs/directive.acledit.html', + replace: true, + scope: { + source: '=', + acls: '=', + writable: '=', + creator: '=' + }, + controller: function($scope, Restangular, $q) { + // Watch for ACLs change and group them for easy displaying + $scope.$watch('acls', function(acls) { + $scope.groupedAcls = _.groupBy(acls, function(acl) { + return acl.id; + }); + }); + + // Initialize add ACL + $scope.acl = { perm: 'READ' }; + + /** + * Delete an ACL. + */ + $scope.deleteAcl = function(acl) { + Restangular.one('acl/' + $scope.source + '/' + acl.perm + '/' + acl.id, null).remove().then(function () { + $scope.acls = _.reject($scope.acls, function(s) { + return angular.equals(acl, s); + }); + }); + }; + + /** + * Add an ACL. + */ + $scope.addAcl = function() { + // Compute ACLs to add + $scope.acl.source = $scope.source; + var acls = []; + if ($scope.acl.perm == 'READWRITE') { + acls = [{ + source: $scope.source, + target: $scope.acl.target.name, + perm: 'READ', + type: $scope.acl.target.type + }, { + source: $scope.source, + target: $scope.acl.target.name, + perm: 'WRITE', + type: $scope.acl.target.type + }]; + } else { + acls = [{ + source: $scope.source, + target: $scope.acl.target.name, + perm: $scope.acl.perm, + type: $scope.acl.target.type + }]; + } + + // Add ACLs + _.each(acls, function(acl) { + Restangular.one('acl').put(acl).then(function(acl) { + if (_.isUndefined(acl.id)) { + return; + } + $scope.acls.push(acl); + $scope.acls = angular.copy($scope.acls); + }); + }); + + // Reset form + $scope.acl = { perm: 'READ' }; + }; + + /** + * Auto-complete on ACL target. + */ + $scope.getTargetAclTypeahead = function($viewValue) { + var deferred = $q.defer(); + Restangular.one('acl/target/search') + .get({ + search: $viewValue + }).then(function(data) { + var output = []; + + // Add the type to use later + output.push.apply(output, _.map(data.users, function(user) { + user.type = 'USER'; + return user; + })); + output.push.apply(output, _.map(data.groups, function(group) { + group.type = 'GROUP'; + return group; + })); + + // Send the data to the typeahead directive + deferred.resolve(output, true); + }); + return deferred.promise; + }; + }, + link: function(scope, element, attr, ctrl) { + } + } +}); \ No newline at end of file diff --git a/docs-web/src/main/webapp/src/app/docs/directive/SelectTag.js b/docs-web/src/main/webapp/src/app/docs/directive/SelectTag.js index c0753325..d61ee60e 100644 --- a/docs-web/src/main/webapp/src/app/docs/directive/SelectTag.js +++ b/docs-web/src/main/webapp/src/app/docs/directive/SelectTag.js @@ -13,9 +13,9 @@ angular.module('docs').directive('selectTag', function() { ref: '@', ngDisabled: '=' }, - controller: function($scope, Tag) { + controller: function($scope, Restangular) { // Retrieve tags - Tag.tags().then(function(data) { + Restangular.one('tag/list').get().then(function(data) { $scope.allTags = data.tags; }); @@ -48,7 +48,7 @@ angular.module('docs').directive('selectTag', function() { if ($event) { $event.preventDefault(); } - } + }; /** * Remove a tag. diff --git a/docs-web/src/main/webapp/src/app/docs/service/Tag.js b/docs-web/src/main/webapp/src/app/docs/service/Tag.js deleted file mode 100644 index 04a94687..00000000 --- a/docs-web/src/main/webapp/src/app/docs/service/Tag.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -/** - * Tag service. - */ -angular.module('docs').factory('Tag', function(Restangular) { - var tags = null; - - return { - /** - * Returns tags. - * @param force If true, force reloading data - */ - tags: function(force) { - return Restangular.one('tag/list').get(); - } - } -}); \ No newline at end of file diff --git a/docs-web/src/main/webapp/src/index.html b/docs-web/src/main/webapp/src/index.html index 3924d6e7..5b92a0b1 100644 --- a/docs-web/src/main/webapp/src/index.html +++ b/docs-web/src/main/webapp/src/index.html @@ -55,6 +55,7 @@ + @@ -73,7 +74,6 @@ - @@ -84,6 +84,7 @@ + diff --git a/docs-web/src/main/webapp/src/partial/docs/directive.acledit.html b/docs-web/src/main/webapp/src/partial/docs/directive.acledit.html new file mode 100644 index 00000000..7e436e50 --- /dev/null +++ b/docs-web/src/main/webapp/src/partial/docs/directive.acledit.html @@ -0,0 +1,61 @@ +
+ + + + + + + + + + +
ForPermission
+ + {{ a.perm }} + + +
+ +
+

Add a permission

+ +
+
+ +
+ +
+
+ + + +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+
\ No newline at end of file diff --git a/docs-web/src/main/webapp/src/partial/docs/document.view.permissions.html b/docs-web/src/main/webapp/src/partial/docs/document.view.permissions.html index 07160373..0c611570 100644 --- a/docs-web/src/main/webapp/src/partial/docs/document.view.permissions.html +++ b/docs-web/src/main/webapp/src/partial/docs/document.view.permissions.html @@ -1,59 +1,4 @@ - - - - - - - - - - -
ForPermission
- - {{ a.perm }} - - -
- -
-

Add a permission

- -
-
- -
- -
-
- - - -
-
- -
- -
- -
-
- -
-
- -
-
-
-
\ No newline at end of file + \ No newline at end of file diff --git a/docs-web/src/main/webapp/src/partial/docs/tag.default.html b/docs-web/src/main/webapp/src/partial/docs/tag.default.html new file mode 100644 index 00000000..dee44dec --- /dev/null +++ b/docs-web/src/main/webapp/src/partial/docs/tag.default.html @@ -0,0 +1,6 @@ +

Tags

+

Tags are labels associated to documents.

+

A document can be tagged by multiple tags, and a tag can be applied to multiple documents.

+

Using the button, you can edit permissions on a tag.

+

If a tag can be read by another user or group, associated documents can also be read by those people.

+

For example, tag your company documents with a tag MyCompany and add the permission Read to a group employees

\ No newline at end of file diff --git a/docs-web/src/main/webapp/src/partial/docs/tag.edit.html b/docs-web/src/main/webapp/src/partial/docs/tag.edit.html new file mode 100644 index 00000000..90f56a22 --- /dev/null +++ b/docs-web/src/main/webapp/src/partial/docs/tag.edit.html @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/docs-web/src/main/webapp/src/partial/docs/tag.html b/docs-web/src/main/webapp/src/partial/docs/tag.html index 733ab8ee..576a8bfa 100644 --- a/docs-web/src/main/webapp/src/partial/docs/tag.html +++ b/docs-web/src/main/webapp/src/partial/docs/tag.html @@ -5,10 +5,9 @@

  + ng-maxlength="36" required ng-model="tag.name" ui-validate="{ space: '!$value || $value.indexOf(\' \') == -1' }"> Add

- This tag already exists Space are not allowed @@ -31,22 +30,15 @@   - + + -
-

{{ tags.length }} tag{{ tags.length > 1 ? 's' : '' }}

-
-
{{ stat.name }} {{ stat.count }}
-
-
-
- -
- +
+
\ No newline at end of file diff --git a/docs-web/src/main/webapp/src/partial/docs/usergroup.default.html b/docs-web/src/main/webapp/src/partial/docs/usergroup.default.html new file mode 100644 index 00000000..5899b461 --- /dev/null +++ b/docs-web/src/main/webapp/src/partial/docs/usergroup.default.html @@ -0,0 +1,2 @@ +

Users & Groups

+

Here you can view informations about users and groups.

\ No newline at end of file diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestAclResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestAclResource.java index bace64ef..e693ea05 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestAclResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestAclResource.java @@ -71,7 +71,7 @@ public class TestAclResource extends BaseJerseyTest { String acl2Id = json.getString("id"); // Add an ACL WRITE for acl2 with acl1 - json = target().path("/acl").request() + target().path("/acl").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acl1Token) .put(Entity.form(new Form() .param("source", document1Id) @@ -80,7 +80,7 @@ public class TestAclResource extends BaseJerseyTest { .param("type", "USER")), JsonObject.class); // Add an ACL WRITE for acl2 with acl1 (again) - json = target().path("/acl").request() + target().path("/acl").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acl1Token) .put(Entity.form(new Form() .param("source", document1Id) @@ -99,7 +99,7 @@ public class TestAclResource extends BaseJerseyTest { String aclGroup2Id = json.getString("id"); // Add an ACL WRITE for aclGroup2 with acl1 - json = target().path("/acl").request() + target().path("/acl").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acl1Token) .put(Entity.form(new Form() .param("source", document1Id) @@ -234,9 +234,9 @@ public class TestAclResource extends BaseJerseyTest { .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acl1Token) .get(JsonObject.class); JsonArray users = json.getJsonArray("users"); - Assert.assertEquals(2, users.size()); + Assert.assertTrue(users.size() > 0); JsonArray groups = json.getJsonArray("groups"); - Assert.assertEquals(1, groups.size()); + Assert.assertTrue(groups.size() > 0); // Search target list (admin) json = target().path("/acl/target/search") @@ -249,4 +249,179 @@ public class TestAclResource extends BaseJerseyTest { groups = json.getJsonArray("groups"); Assert.assertEquals(1, groups.size()); } + + @Test + public void testAclTags() { + // Login acltag1 + clientUtil.createUser("acltag1"); + String acltag1Token = clientUtil.login("acltag1"); + + // Login acltag2 + clientUtil.createUser("acltag2"); + String acltag2Token = clientUtil.login("acltag2"); + + // Create tag1 with acltag1 + JsonObject json = target().path("/tag").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag1Token) + .put(Entity.form(new Form() + .param("name", "AclTag1") + .param("color", "#ff0000")), JsonObject.class); + String tag1Id = json.getString("id"); + Assert.assertNotNull(tag1Id); + + // Create document1 with acltag1 tagged with tag1 + json = target().path("/document").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag1Token) + .put(Entity.form(new Form() + .param("title", "My super document 1") + .param("tags", tag1Id) + .param("language", "eng")), JsonObject.class); + String document1Id = json.getString("id"); + + // acltag2 cannot see document1 + Response response = target().path("/document/" + document1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(); + Assert.assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus()); + + // acltag2 cannot see any tag + json = target().path("/tag/list").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(JsonObject.class); + JsonArray tags = json.getJsonArray("tags"); + Assert.assertEquals(0, tags.size()); + + // acltag2 cannot see tag1 + response = target().path("/tag/" + tag1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(); + Assert.assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus()); + + // acltag2 cannot see any document + json = target().path("/document/list") + .queryParam("sort_column", 3) + .queryParam("asc", true) + .request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(JsonObject.class); + JsonArray documents = json.getJsonArray("documents"); + Assert.assertEquals(0, documents.size()); + + // acltag2 cannot edit tag1 + response = target().path("/tag/" + tag1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .post(Entity.form(new Form() + .param("name", "AclTag1") + .param("color", "#ff0000"))); + Assert.assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus()); + + // acltag2 cannot edit document1 + response = target().path("/document/" + document1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .post(Entity.form(new Form() + .param("title", "My super document 1") + .param("tags", tag1Id) + .param("language", "eng"))); + Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus()); + + // Add an ACL READ for acltag2 with acltag1 on tag1 + target().path("/acl").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag1Token) + .put(Entity.form(new Form() + .param("source", tag1Id) + .param("perm", "READ") + .param("target", "acltag2") + .param("type", "USER")), JsonObject.class); + + // acltag2 can see tag1 + json = target().path("/tag/" + tag1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(JsonObject.class); + Assert.assertFalse(json.getBoolean("writable")); + Assert.assertEquals(3, json.getJsonArray("acls").size()); + + // acltag2 still cannot edit tag1 + response = target().path("/tag/" + tag1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .post(Entity.form(new Form() + .param("name", "AclTag1") + .param("color", "#ff0000"))); + Assert.assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus()); + + // acltag2 still cannot edit document1 + response = target().path("/document/" + document1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .post(Entity.form(new Form() + .param("title", "My super document 1") + .param("tags", tag1Id) + .param("language", "eng"))); + Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus()); + + // acltag2 can see document1 with tag1 (non-writable) + json = target().path("/document/" + document1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(JsonObject.class); + tags = json.getJsonArray("tags"); + Assert.assertEquals(1, tags.size()); + Assert.assertFalse(json.getBoolean("writable")); + Assert.assertEquals(tag1Id, tags.getJsonObject(0).getString("id")); + + // acltag2 can see tag1 + json = target().path("/tag/list").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(JsonObject.class); + tags = json.getJsonArray("tags"); + Assert.assertEquals(1, tags.size()); + Assert.assertEquals(tag1Id, tags.getJsonObject(0).getString("id")); + + // acltag2 can see exactly one document + json = target().path("/document/list") + .queryParam("sort_column", 3) + .queryParam("asc", true) + .request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(JsonObject.class); + documents = json.getJsonArray("documents"); + Assert.assertEquals(1, documents.size()); + + // Add an ACL WRITE for acltag2 with acltag1 on tag1 + target().path("/acl").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag1Token) + .put(Entity.form(new Form() + .param("source", tag1Id) + .param("perm", "WRITE") + .param("target", "acltag2") + .param("type", "USER")), JsonObject.class); + + // acltag2 can see document1 with tag1 (writable) + json = target().path("/document/" + document1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(JsonObject.class); + tags = json.getJsonArray("tags"); + Assert.assertEquals(1, tags.size()); + Assert.assertTrue(json.getBoolean("writable")); + Assert.assertEquals(tag1Id, tags.getJsonObject(0).getString("id")); + + // acltag2 can see and edit tag1 + json = target().path("/tag/" + tag1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .get(JsonObject.class); + Assert.assertTrue(json.getBoolean("writable")); + Assert.assertEquals(4, json.getJsonArray("acls").size()); + + // acltag2 can edit tag1 + target().path("/tag/" + tag1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .post(Entity.form(new Form() + .param("name", "AclTag1") + .param("color", "#ff0000")), JsonObject.class); + + // acltag2 can edit document1 + target().path("/document/" + document1Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token) + .post(Entity.form(new Form() + .param("title", "My super document 1") + .param("tags", tag1Id) + .param("language", "eng")), JsonObject.class); + } } \ No newline at end of file diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestAppResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestAppResource.java index 4a8df5b0..e8fc889c 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestAppResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestAppResource.java @@ -2,11 +2,17 @@ package com.sismics.docs.rest; import javax.json.JsonArray; import javax.json.JsonObject; +import javax.persistence.EntityManager; +import javax.persistence.EntityTransaction; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Form; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import com.sismics.docs.core.constant.PermType; +import com.sismics.docs.core.dao.jpa.AclDao; +import com.sismics.util.context.ThreadLocalContext; +import com.sismics.util.jpa.EMF; import org.junit.Assert; import org.junit.Test; @@ -57,6 +63,35 @@ public class TestAppResource extends BaseJerseyTest { .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) .post(Entity.form(new Form())); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + + // Create a tag + json = target().path("/tag").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) + .put(Entity.form(new Form() + .param("name", "Tag4") + .param("color", "#00ff00")), JsonObject.class); + String tagId = json.getString("id"); + + // Init transactional context + EntityManager em = EMF.get().createEntityManager(); + ThreadLocalContext context = ThreadLocalContext.get(); + context.setEntityManager(em); + EntityTransaction tx = em.getTransaction(); + tx.begin(); + + // Remove base ACLs + AclDao aclDao = new AclDao(); + aclDao.delete(tagId, PermType.READ, "admin", "admin"); + aclDao.delete(tagId, PermType.WRITE, "admin", "admin"); + Assert.assertEquals(0, aclDao.getBySourceId(tagId).size()); + tx.commit(); + + // Add base ACLs to tags + response = target().path("/app/batch/tag_acls").request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) + .post(Entity.form(new Form())); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + Assert.assertEquals(2, aclDao.getBySourceId(tagId).size()); } /** diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestDocumentResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestDocumentResource.java index 4f7b4ee8..d507c74a 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestDocumentResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestDocumentResource.java @@ -87,7 +87,7 @@ public class TestDocumentResource extends BaseJerseyTest { Assert.assertNotNull(document2Id); // Add a file - String file1Id = null; + String file1Id; try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) { StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png"); try (FormDataMultiPart multiPart = new FormDataMultiPart()) { @@ -103,7 +103,7 @@ public class TestDocumentResource extends BaseJerseyTest { } // Share this document - json = target().path("/share").request() + target().path("/share").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, document1Token) .put(Entity.form(new Form().param("id", document1Id)), JsonObject.class); @@ -148,7 +148,7 @@ public class TestDocumentResource extends BaseJerseyTest { Assert.assertNotNull(document3Id); // Add a file - String file3Id = null; + String file3Id; try (InputStream is = Resources.getResource("file/Einstein-Roosevelt-letter.png").openStream()) { StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "Einstein-Roosevelt-letter.png"); try (FormDataMultiPart multiPart = new FormDataMultiPart()) { @@ -393,7 +393,7 @@ public class TestDocumentResource extends BaseJerseyTest { Assert.assertNotNull(document1Id); // Add a PDF file - String file1Id = null; + String file1Id; try (InputStream is = Resources.getResource("file/document.odt").openStream()) { StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "document.odt"); try (FormDataMultiPart multiPart = new FormDataMultiPart()) { @@ -452,7 +452,7 @@ public class TestDocumentResource extends BaseJerseyTest { Assert.assertNotNull(document1Id); // Add a PDF file - String file1Id = null; + String file1Id; try (InputStream is = Resources.getResource("file/document.docx").openStream()) { StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "document.docx"); try (FormDataMultiPart multiPart = new FormDataMultiPart()) { @@ -511,7 +511,7 @@ public class TestDocumentResource extends BaseJerseyTest { Assert.assertNotNull(document1Id); // Add a PDF file - String file1Id = null; + String file1Id; try (InputStream is = Resources.getResource("file/wikipedia.pdf").openStream()) { StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart("file", is, "wikipedia.pdf"); try (FormDataMultiPart multiPart = new FormDataMultiPart()) { diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestGroupResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestGroupResource.java index c37ad04c..1d5d37a9 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestGroupResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestGroupResource.java @@ -106,7 +106,7 @@ public class TestGroupResource extends BaseJerseyTest { Assert.assertEquals(1, users.size()); // Add group1 to g112 (again) - json = target().path("/group/g112").request() + target().path("/group/g112").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) .put(Entity.form(new Form() .param("username", "group1")), JsonObject.class); @@ -145,7 +145,7 @@ public class TestGroupResource extends BaseJerseyTest { Assert.assertEquals("group1", members.getString(0)); // Remove group1 from g12new - json = target().path("/group/g12new/group1").request() + target().path("/group/g12new/group1").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) .delete(JsonObject.class); @@ -164,7 +164,7 @@ public class TestGroupResource extends BaseJerseyTest { Assert.assertTrue(groupList.contains("g112")); // Delete group g1 - json = target().path("/group/g1").request() + target().path("/group/g1").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken) .delete(JsonObject.class); diff --git a/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java b/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java index 597074da..3724d848 100644 --- a/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java @@ -46,6 +46,17 @@ public class TestTagResource extends BaseJerseyTest { .param("parent", tag3Id)), JsonObject.class); String tag4Id = json.getString("id"); Assert.assertNotNull(tag4Id); + + // Get the tag + json = target().path("/tag/" + tag4Id).request() + .cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token) + .get(JsonObject.class); + Assert.assertEquals("Tag4", json.getString("name")); + Assert.assertEquals("tag1", json.getString("creator")); + Assert.assertEquals("#00ff00", json.getString("color")); + Assert.assertTrue(json.getBoolean("writable")); + JsonArray acls = json.getJsonArray("acls"); + Assert.assertEquals(2, acls.size()); // Create a tag with space (not allowed) Response response = target().path("/tag").request() @@ -55,7 +66,7 @@ public class TestTagResource extends BaseJerseyTest { Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus())); // Create a document - json = target().path("/document").request() + target().path("/document").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token) .put(Entity.form(new Form() .param("title", "My super document 1") @@ -115,15 +126,6 @@ public class TestTagResource extends BaseJerseyTest { Assert.assertEquals(1, tags.size()); Assert.assertEquals(tag4Id, tags.getJsonObject(0).getString("id")); - // Get tag stats - json = target().path("/tag/stats").request() - .cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token) - .get(JsonObject.class); - JsonArray stats = json.getJsonArray("stats"); - Assert.assertTrue(stats.size() == 2); - Assert.assertEquals(1, stats.getJsonObject(0).getInt("count")); - Assert.assertEquals(1, stats.getJsonObject(1).getInt("count")); - // Get all tags json = target().path("/tag/list").request() .cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token) @@ -150,7 +152,7 @@ public class TestTagResource extends BaseJerseyTest { Assert.assertTrue(tags.size() > 0); Assert.assertEquals("UpdatedName", tags.getJsonObject(1).getString("name")); Assert.assertEquals("#0000ff", tags.getJsonObject(1).getString("color")); - Assert.assertEquals(JsonValue.NULL, tags.getJsonObject(1).get("parent")); + Assert.assertNull(tags.getJsonObject(1).get("parent")); // Deletes a tag target().path("/tag/" + tag4Id).request() diff --git a/docs-parent/pom.xml b/pom.xml similarity index 98% rename from docs-parent/pom.xml rename to pom.xml index 81362ea6..64c8b8e7 100644 --- a/docs-parent/pom.xml +++ b/pom.xml @@ -120,9 +120,9 @@ - ../docs-core - ../docs-web-common - ../docs-web + docs-core + docs-web-common + docs-web @@ -374,8 +374,9 @@ org.apache.poi.xwpf.converter.pdf ${fr.opensagres.xdocreport.version} - - + + + com.twelvemonkeys.servlet servlet ${com.twelvemonkeys.imageio.version} @@ -389,7 +390,8 @@ - + + com.twelvemonkeys.imageio imageio-jpeg ${com.twelvemonkeys.imageio.version}