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 e71d9395..34f9155e 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 @@ -127,6 +127,8 @@ public class DocumentDao { parameterMap.put("search", "%" + criteria.getSearch() + "%"); } + criteriaList.add("d.DOC_DELETEDATE_D is null"); + if (!criteriaList.isEmpty()) { sb.append(" where "); sb.append(Joiner.on(" and ").join(criteriaList)); 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 new file mode 100644 index 00000000..c2b18796 --- /dev/null +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/TagDao.java @@ -0,0 +1,182 @@ +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 javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.Query; + +import com.sismics.docs.core.dao.jpa.dto.TagDto; +import com.sismics.docs.core.model.jpa.DocumentTag; +import com.sismics.docs.core.model.jpa.Tag; +import com.sismics.util.context.ThreadLocalContext; + +/** + * Tag DAO. + * + * @author bgamard + */ +public class TagDao { + /** + * Gets a tag by its ID. + * + * @param id Tag ID + * @return Tag + */ + public Tag getById(String id) { + EntityManager em = ThreadLocalContext.get().getEntityManager(); + try { + return em.find(Tag.class, id); + } catch (NoResultException e) { + return null; + } + } + + /** + * 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.id"); + q.setParameter("userId", userId); + return q.getResultList(); + } + + /** + * Update tags on a document. + * + * @param documentId + * @param tagIdSet + */ + public void updateTagList(String documentId, Set tagIdSet) { + // Delete old tag links + EntityManager em = ThreadLocalContext.get().getEntityManager(); + Query q = em.createQuery("delete DocumentTag dt where dt.documentId = :documentId"); + q.setParameter("documentId", documentId); + q.executeUpdate(); + + // Create new tag links + for (String tagId : tagIdSet) { + DocumentTag documentTag = new DocumentTag(); + documentTag.setId(UUID.randomUUID().toString()); + documentTag.setDocumentId(documentId); + documentTag.setTagId(tagId); + em.persist(documentTag); + } + + } + + /** + * Returns tag list on a document. + * @param documentId + * @return + */ + @SuppressWarnings("unchecked") + public List getByDocumentId(String documentId) { + EntityManager em = ThreadLocalContext.get().getEntityManager(); + StringBuilder sb = new StringBuilder("select t.TAG_ID_C, t.TAG_NAME_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 "); + + // Perform the query + Query q = em.createNativeQuery(sb.toString()); + q.setParameter("documentId", documentId); + 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++]); + tagDtoList.add(tagDto); + } + return tagDtoList; + } + + /** + * Creates a new tag. + * + * @param tag Tag + * @return New ID + * @throws Exception + */ + public String create(Tag tag) { + // Create the UUID + tag.setId(UUID.randomUUID().toString()); + + // Create the tag + EntityManager em = ThreadLocalContext.get().getEntityManager(); + tag.setCreateDate(new Date()); + em.persist(tag); + + return tag.getId(); + } + + /** + * Returns a tag by user ID and name. + * @param userId User ID + * @param name Name + * @return Tag + */ + public Tag getByUserIdAndName(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 user ID and name. + * @param userId User ID + * @param tagId Tag ID + * @return Tag + */ + public Tag getByUserIdAndTagId(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. + * + * @param tagId Tag ID + */ + public void delete(String tagId) { + EntityManager em = ThreadLocalContext.get().getEntityManager(); + + // Get the tag + Query q = em.createQuery("select t from Tag t where t.id = :id and t.deleteDate is null"); + q.setParameter("id", tagId); + Tag tagDb = (Tag) q.getSingleResult(); + + // Delete the tag + Date dateNow = new Date(); + tagDb.setDeleteDate(dateNow); + + // Delete linked data + q = em.createQuery("delete DocumentTag dt where dt.tagId = :tagId"); + q.setParameter("tagId", tagId); + q.executeUpdate(); + } +} diff --git a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/DocumentDto.java b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/DocumentDto.java index 09fa2074..2bca5d21 100644 --- a/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/DocumentDto.java +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/DocumentDto.java @@ -5,7 +5,7 @@ import javax.persistence.Id; /** * Document DTO. * - * @author jtremeaux + * @author bgamard */ public class DocumentDto { /** 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 new file mode 100644 index 00000000..56322ad4 --- /dev/null +++ b/docs-core/src/main/java/com/sismics/docs/core/dao/jpa/dto/TagDto.java @@ -0,0 +1,57 @@ +package com.sismics.docs.core.dao.jpa.dto; + +import javax.persistence.Id; + +/** + * Tag DTO. + * + * @author bgamard + */ +public class TagDto { + /** + * Tag ID. + */ + @Id + private String id; + + /** + * Name. + */ + private String name; + + /** + * Getter of id. + * + * @return the id + */ + public String getId() { + return id; + } + + /** + * Setter of id. + * + * @param id id + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter of name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Setter of name. + * + * @param name name + */ + public void setName(String name) { + this.name = name; + } +} diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/DocumentTag.java b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/DocumentTag.java new file mode 100644 index 00000000..6d4afd2a --- /dev/null +++ b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/DocumentTag.java @@ -0,0 +1,107 @@ +package com.sismics.docs.core.model.jpa; + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.google.common.base.Objects; + +/** + * Link between a document and a tag. + * + * @author bgamard + */ +@Entity +@Table(name = "T_DOCUMENT_TAG") +public class DocumentTag implements Serializable { + /** + * Serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * Document tag ID. + */ + @Id + @Column(name = "DOT_ID_C", length = 36) + private String id; + + /** + * Document ID. + */ + @Id + @Column(name = "DOT_IDDOCUMENT_C", length = 36) + private String documentId; + + /** + * Tag ID. + */ + @Id + @Column(name = "DOT_IDTAG_C", length = 36) + private String tagId; + + /** + * Getter of id. + * + * @return id + */ + public String getId() { + return id; + } + + /** + * Setter of id. + * + * @param id id + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter de documentId. + * + * @return the documentId + */ + public String getDocumentId() { + return documentId; + } + + /** + * Setter de documentId. + * + * @param documentId documentId + */ + public void setDocumentId(String documentId) { + this.documentId = documentId; + } + + /** + * Getter de tagId. + * + * @return the tagId + */ + public String getTagId() { + return tagId; + } + + /** + * Setter de tagId. + * + * @param tagId tagId + */ + public void setTagId(String tagId) { + this.tagId = tagId; + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("documentId", documentId) + .add("tagId", tagId) + .toString(); + } +} diff --git a/docs-core/src/main/java/com/sismics/docs/core/model/jpa/Tag.java b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/Tag.java new file mode 100644 index 00000000..e5da92c4 --- /dev/null +++ b/docs-core/src/main/java/com/sismics/docs/core/model/jpa/Tag.java @@ -0,0 +1,148 @@ +package com.sismics.docs.core.model.jpa; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.google.common.base.Objects; + +/** + * Tag. + * + * @author bgamard + */ +@Entity +@Table(name = "T_TAG") +public class Tag { + /** + * Tag ID. + */ + @Id + @Column(name = "TAG_ID_C", length = 36) + private String id; + + /** + * Tag name. + */ + @Column(name = "TAG_NAME_C", nullable = false, length = 36) + private String name; + + /** + * User ID. + */ + @Column(name = "TAG_IDUSER_C", nullable = false, length = 36) + private String userId; + + /** + * Creation date. + */ + @Column(name = "TAG_CREATEDATE_D", nullable = false) + private Date createDate; + + /** + * Deletion date. + */ + @Column(name = "TAG_DELETEDATE_D") + private Date deleteDate; + + /** + * Getter of id. + * + * @return id + */ + public String getId() { + return id; + } + + /** + * Setter of id. + * + * @param id id + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter of userId. + * + * @return the userId + */ + public String getUserId() { + return userId; + } + + /** + * Setter of userId. + * + * @param userId userId + */ + public void setUserId(String userId) { + this.userId = userId; + } + + /** + * Getter of name. + * + * @return name + */ + public String getName() { + return name; + } + + /** + * Setter of name. + * + * @param name name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Getter of createDate. + * + * @return createDate + */ + public Date getCreateDate() { + return createDate; + } + + /** + * Setter of createDate. + * + * @param createDate createDate + */ + public void setCreateDate(Date createDate) { + this.createDate = createDate; + } + + /** + * Getter of deleteDate. + * + * @return deleteDate + */ + public Date getDeleteDate() { + return deleteDate; + } + + /** + * Setter of deleteDate. + * + * @param deleteDate deleteDate + */ + public void setDeleteDate(Date deleteDate) { + this.deleteDate = deleteDate; + } + + @Override + public String toString() { + return Objects.toStringHelper(this) + .add("id", id) + .add("name", name) + .toString(); + } +} diff --git a/docs-core/src/main/resources/META-INF/persistence.xml b/docs-core/src/main/resources/META-INF/persistence.xml index a2474e3f..9c684e78 100644 --- a/docs-core/src/main/resources/META-INF/persistence.xml +++ b/docs-core/src/main/resources/META-INF/persistence.xml @@ -13,5 +13,7 @@ com.sismics.docs.core.model.jpa.User com.sismics.docs.core.model.jpa.RoleBaseFunction com.sismics.docs.core.model.jpa.Document + com.sismics.docs.core.model.jpa.Tag + com.sismics.docs.core.model.jpa.DocumentTag \ No newline at end of file diff --git a/docs-core/src/main/resources/db/update/dbupdate-000-0.sql b/docs-core/src/main/resources/db/update/dbupdate-000-0.sql index c865937d..33198eb1 100644 --- a/docs-core/src/main/resources/db/update/dbupdate-000-0.sql +++ b/docs-core/src/main/resources/db/update/dbupdate-000-0.sql @@ -8,11 +8,16 @@ create cached table T_DOCUMENT ( DOC_ID_C varchar(36) not null, DOC_IDUSER_C var create memory table T_USER ( USE_ID_C varchar(36) not null, USE_IDLOCALE_C varchar(10) not null, USE_IDROLE_C varchar(36) not null, USE_USERNAME_C varchar(50) not null, USE_PASSWORD_C varchar(60) not null, USE_EMAIL_C varchar(100) not null, USE_THEME_C varchar(100) not null, USE_FIRSTCONNECTION_B bit not null, USE_CREATEDATE_D datetime not null, USE_DELETEDATE_D datetime, primary key (USE_ID_C) ); create memory table T_ROLE ( ROL_ID_C varchar(36) not null, ROL_NAME_C varchar(36) not null, ROL_CREATEDATE_D datetime not null, ROL_DELETEDATE_D datetime, primary key (ROL_ID_C) ); create memory table T_ROLE_BASE_FUNCTION ( RBF_ID_C varchar(36) not null, RBF_IDROLE_C varchar(36) not null, RBF_IDBASEFUNCTION_C varchar(20) not null, RBF_CREATEDATE_D datetime not null, RBF_DELETEDATE_D datetime, primary key (RBF_ID_C) ); +create cached table T_TAG ( TAG_ID_C varchar(36) not null, TAG_IDUSER_C varchar(36) not null, TAG_NAME_C varchar(36) not null, TAG_CREATEDATE_D datetime, TAG_DELETEDATE_D datetime, primary key (TAG_ID_C) ); +create cached table T_DOCUMENT_TAG ( DOT_ID_C varchar(36) not null, DOT_IDDOCUMENT_C varchar(36) not null, DOT_IDTAG_C varchar(36) not null, primary key (DOT_ID_C) ); alter table T_AUTHENTICATION_TOKEN add constraint FK_AUT_IDUSER_C foreign key (AUT_IDUSER_C) references T_USER (USE_ID_C) on delete restrict on update restrict; alter table T_DOCUMENT add constraint FK_DOC_IDUSER_C foreign key (DOC_IDUSER_C) references T_USER (USE_ID_C) on delete restrict on update restrict; alter table T_FILE add constraint FK_FIL_IDDOC_C foreign key (FIL_IDDOC_C) references T_DOCUMENT (DOC_ID_C) on delete restrict on update restrict; alter table T_USER add constraint FK_USE_IDLOCALE_C foreign key (USE_IDLOCALE_C) references T_LOCALE (LOC_ID_C) on delete restrict on update restrict; alter table T_USER add constraint FK_USE_IDROLE_C foreign key (USE_IDROLE_C) references T_ROLE (ROL_ID_C) on delete restrict on update restrict; +alter table T_TAG add constraint FK_TAG_IDUSER_C foreign key (TAG_IDUSER_C) references T_USER (USE_ID_C) on delete restrict on update restrict; +alter table T_DOCUMENT_TAG add constraint FK_DOT_IDDOCUMENT_C foreign key (DOT_IDDOCUMENT_C) references T_DOCUMENT (DOC_ID_C) on delete restrict on update restrict; +alter table T_DOCUMENT_TAG add constraint FK_DOT_IDTAG_C foreign key (DOT_IDTAG_C) references T_TAG (TAG_ID_C) on delete restrict on update restrict; alter table T_ROLE_BASE_FUNCTION add constraint FK_RBF_IDROLE_C foreign key (RBF_IDROLE_C) references T_ROLE (ROL_ID_C) on delete restrict on update restrict; alter table T_ROLE_BASE_FUNCTION add constraint FK_RBF_IDBASEFUNCTION_C foreign key (RBF_IDBASEFUNCTION_C) references T_BASE_FUNCTION (BAF_ID_C) on delete restrict on update restrict; insert into T_CONFIG(CFG_ID_C, CFG_VALUE_C) values('DB_VERSION', '0'); 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 e645a6cc..29de6fd4 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 @@ -2,7 +2,9 @@ package com.sismics.docs.rest.resource; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.persistence.NoResultException; import javax.ws.rs.DELETE; @@ -22,9 +24,12 @@ import org.codehaus.jettison.json.JSONObject; import com.google.common.base.Strings; import com.sismics.docs.core.dao.jpa.DocumentDao; +import com.sismics.docs.core.dao.jpa.TagDao; import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria; import com.sismics.docs.core.dao.jpa.dto.DocumentDto; +import com.sismics.docs.core.dao.jpa.dto.TagDto; import com.sismics.docs.core.model.jpa.Document; +import com.sismics.docs.core.model.jpa.Tag; import com.sismics.docs.core.util.jpa.PaginatedList; import com.sismics.docs.core.util.jpa.PaginatedLists; import com.sismics.docs.core.util.jpa.SortCriteria; @@ -69,6 +74,18 @@ public class DocumentResource extends BaseResource { document.put("description", documentDb.getDescription()); document.put("create_date", documentDb.getCreateDate().getTime()); + // Get tags + TagDao tagDao = new TagDao(); + List tagDtoList = tagDao.getByDocumentId(id); + List tags = new ArrayList(); + for (TagDto tagDto : tagDtoList) { + JSONObject tag = new JSONObject(); + tag.put("id", tagDto.getId()); + tag.put("name", tagDto.getName()); + tags.add(tag); + } + document.put("tags", tags); + return Response.ok().entity(document).build(); } @@ -132,7 +149,8 @@ public class DocumentResource extends BaseResource { @Produces(MediaType.APPLICATION_JSON) public Response add( @FormParam("title") String title, - @FormParam("description") String description) throws JSONException { + @FormParam("description") String description, + @FormParam("tags[]") List tagList) throws JSONException { if (!authenticate()) { throw new ForbiddenClientException(); } @@ -149,6 +167,9 @@ public class DocumentResource extends BaseResource { document.setDescription(description); String documentId = documentDao.create(document); + // Update tags + updateTagList(documentId, tagList); + JSONObject response = new JSONObject(); response.put("id", documentId); return Response.ok().entity(response).build(); @@ -168,7 +189,8 @@ public class DocumentResource extends BaseResource { public Response update( @PathParam("id") String id, @FormParam("title") String title, - @FormParam("description") String description) throws JSONException { + @FormParam("description") String description, + @FormParam("tags[]") List tagList) throws JSONException { if (!authenticate()) { throw new ForbiddenClientException(); } @@ -194,11 +216,40 @@ public class DocumentResource extends BaseResource { document.setDescription(description); } + // Update tags + updateTagList(id, tagList); + // Always return ok JSONObject response = new JSONObject(); response.put("id", id); return Response.ok().entity(response).build(); } + + /** + * Update tags list on a document. + * + * @param documentId + * @param tagList + * @throws JSONException + */ + private void updateTagList(String documentId, List tagList) throws JSONException { + if (tagList != null) { + 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()); + } + for (String tagId : tagList) { + if (!tagIdSet.contains(tagId)) { + throw new ClientException("TagNotFound", MessageFormat.format("Tag not found: {0}", tagId)); + } + tagSet.add(tagId); + } + tagDao.updateTagList(documentId, tagSet); + } + } /** * Deletes a document. 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 new file mode 100644 index 00000000..8213dcc7 --- /dev/null +++ b/docs-web/src/main/java/com/sismics/docs/rest/resource/TagResource.java @@ -0,0 +1,126 @@ +package com.sismics.docs.rest.resource; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +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.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; + +import com.sismics.docs.core.dao.jpa.TagDao; +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.ValidationUtil; + +/** + * Tag REST resources. + * + * @author bgamard + */ +@Path("/tag") +public class TagResource extends BaseResource { + /** + * Returns the list of all tags. + * + * @return Response + * @throws JSONException + */ + @GET + @Path("/list") + @Produces(MediaType.APPLICATION_JSON) + public Response list() throws JSONException { + if (!authenticate()) { + throw new ForbiddenClientException(); + } + + TagDao tagDao = new TagDao(); + List tagList = tagDao.getByUserId(principal.getId()); + JSONObject response = new JSONObject(); + List items = new ArrayList(); + for (Tag tag : tagList) { + JSONObject item = new JSONObject(); + item.put("id", tag.getId()); + items.add(item); + } + response.put("tags", items); + return Response.ok().entity(response).build(); + } + + /** + * Creates a new tag. + * + * @param name Name + * @return Response + * @throws JSONException + */ + @PUT + @Produces(MediaType.APPLICATION_JSON) + public Response add( + @FormParam("name") String name) throws JSONException { + if (!authenticate()) { + throw new ForbiddenClientException(); + } + + // Validate input data + name = ValidationUtil.validateLength(name, "title", 1, 36, false); + + // Get the tag + TagDao tagDao = new TagDao(); + Tag tag = tagDao.getByUserIdAndName(principal.getId(), name); + if (tag != null) { + throw new ClientException("AlreadyExistingTag", MessageFormat.format("Tag already exists: {0}", name)); + } + + // Create the tag + tag = new Tag(); + tag.setName(name); + tag.setUserId(principal.getId()); + String tagId = tagDao.create(tag); + + JSONObject response = new JSONObject(); + response.put("id", tagId); + return Response.ok().entity(response).build(); + } + + /** + * Delete a tag. + * + * @param name Name + * @return Response + * @throws JSONException + */ + @DELETE + @Path("{id: [a-z0-9\\-]+}") + @Produces(MediaType.APPLICATION_JSON) + public Response delete( + @PathParam("id") String tagId) throws JSONException { + if (!authenticate()) { + throw new ForbiddenClientException(); + } + + // Get the tag + TagDao tagDao = new TagDao(); + Tag tag = tagDao.getByUserIdAndTagId(principal.getId(), tagId); + if (tag == null) { + throw new ClientException("TagNotFound", MessageFormat.format("Tag not found: {0}", tagId)); + } + + // Delete the tag + tagDao.delete(tagId); + + JSONObject response = new JSONObject(); + response.put("status", "ok"); + return Response.ok().entity(response).build(); + } +} diff --git a/docs-web/src/main/webapp/js/controller/DocumentView.js b/docs-web/src/main/webapp/js/controller/DocumentView.js index c2c516ad..0b0b8c76 100644 --- a/docs-web/src/main/webapp/js/controller/DocumentView.js +++ b/docs-web/src/main/webapp/js/controller/DocumentView.js @@ -37,6 +37,26 @@ App.controller('DocumentView', function($rootScope, $scope, $state, $stateParams $state.transitionTo('document.view.file', { id: $stateParams.id, fileId: file.id }) }; + /** + * Delete a document. + */ + $scope.deleteDocument = function(document) { + var title = 'Delete document'; + var msg = 'Do you really want to delete this document?'; + var btns = [{result:'cancel', label: 'Cancel'}, {result:'ok', label: 'OK', cssClass: 'btn-primary'}]; + + $dialog.messageBox(title, msg, btns) + .open() + .then(function(result) { + if (result == 'ok') { + Restangular.one('document', document.id).remove().then(function() { + $scope.loadDocuments(); + $state.transitionTo('document.default'); + }); + } + }); + }; + /** * Delete a file. */ diff --git a/docs-web/src/main/webapp/partial/document.view.html b/docs-web/src/main/webapp/partial/document.view.html index c1cf6922..689f25d3 100644 --- a/docs-web/src/main/webapp/partial/document.view.html +++ b/docs-web/src/main/webapp/partial/document.view.html @@ -1,5 +1,6 @@
+
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 6bb34940..dee3bd76 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 @@ -30,15 +30,27 @@ public class TestDocumentResource extends BaseJerseyTest { clientUtil.createUser("document1"); String document1Token = clientUtil.login("document1"); + // Create a tag + WebResource tagResource = resource().path("/tag"); + tagResource.addFilter(new CookieAuthenticationFilter(document1Token)); + MultivaluedMapImpl postParams = new MultivaluedMapImpl(); + postParams.add("name", "Super tag"); + ClientResponse response = tagResource.put(ClientResponse.class, postParams); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + JSONObject json = response.getEntity(JSONObject.class); + String tag1Id = json.optString("id"); + Assert.assertNotNull(tag1Id); + // Create a document WebResource documentResource = resource().path("/document"); documentResource.addFilter(new CookieAuthenticationFilter(document1Token)); - MultivaluedMapImpl postParams = new MultivaluedMapImpl(); + postParams = new MultivaluedMapImpl(); postParams.add("title", "My super document 1"); postParams.add("description", "My super description for document 1"); - ClientResponse response = documentResource.put(ClientResponse.class, postParams); + postParams.add("tags[]", tag1Id); + response = documentResource.put(ClientResponse.class, postParams); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); - JSONObject json = response.getEntity(JSONObject.class); + json = response.getEntity(JSONObject.class); String document1Id = json.optString("id"); Assert.assertNotNull(document1Id); @@ -85,6 +97,20 @@ public class TestDocumentResource extends BaseJerseyTest { json = response.getEntity(JSONObject.class); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); Assert.assertEquals(document1Id, json.getString("id")); + JSONArray tags = json.getJSONArray("tags"); + Assert.assertEquals(1, tags.length()); + Assert.assertEquals(tag1Id, tags.getJSONObject(0).getString("id")); + + // Create a tag + tagResource = resource().path("/tag"); + tagResource.addFilter(new CookieAuthenticationFilter(document1Token)); + postParams = new MultivaluedMapImpl(); + postParams.add("name", "Super tag 2"); + response = tagResource.put(ClientResponse.class, postParams); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + json = response.getEntity(JSONObject.class); + String tag2Id = json.optString("id"); + Assert.assertNotNull(tag1Id); // Update a document documentResource = resource().path("/document/" + document1Id); @@ -92,6 +118,7 @@ public class TestDocumentResource extends BaseJerseyTest { postParams = new MultivaluedMapImpl(); postParams.add("title", "My new super document 1"); postParams.add("description", "My new super description for document 1"); + postParams.add("tags[]", tag2Id); response = documentResource.post(ClientResponse.class, postParams); Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); json = response.getEntity(JSONObject.class); @@ -105,6 +132,9 @@ public class TestDocumentResource extends BaseJerseyTest { Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); Assert.assertTrue(json.getString("title").contains("new")); Assert.assertTrue(json.getString("description").contains("new")); + tags = json.getJSONArray("tags"); + Assert.assertEquals(1, tags.length()); + Assert.assertEquals(tag2Id, tags.getJSONObject(0).getString("id")); // Deletes a document documentResource = resource().path("/document/" + document1Id); 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 new file mode 100644 index 00000000..d3dad245 --- /dev/null +++ b/docs-web/src/test/java/com/sismics/docs/rest/TestTagResource.java @@ -0,0 +1,71 @@ +package com.sismics.docs.rest; + +import junit.framework.Assert; + +import org.codehaus.jettison.json.JSONArray; +import org.codehaus.jettison.json.JSONException; +import org.codehaus.jettison.json.JSONObject; +import org.junit.Test; + +import com.sismics.docs.rest.BaseJerseyTest; +import com.sismics.docs.rest.filter.CookieAuthenticationFilter; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.ClientResponse.Status; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.core.util.MultivaluedMapImpl; + +/** + * Test the tag resource. + * + * @author bgamard + */ +public class TestTagResource extends BaseJerseyTest { + /** + * Test the tag resource. + * + * @throws JSONException + */ + @Test + public void testTagResource() throws JSONException { + // Login tag1 + clientUtil.createUser("tag1"); + String tag1Token = clientUtil.login("tag1"); + + // Create a tag + WebResource tagResource = resource().path("/tag"); + tagResource.addFilter(new CookieAuthenticationFilter(tag1Token)); + MultivaluedMapImpl postParams = new MultivaluedMapImpl(); + postParams.add("name", "Tag 3"); + ClientResponse response = tagResource.put(ClientResponse.class, postParams); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + JSONObject json = response.getEntity(JSONObject.class); + String tag3Id = json.optString("id"); + Assert.assertNotNull(tag3Id); + + // Get all tags + tagResource = resource().path("/tag/list"); + tagResource.addFilter(new CookieAuthenticationFilter(tag1Token)); + response = tagResource.get(ClientResponse.class); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + json = response.getEntity(JSONObject.class); + JSONArray tags = json.getJSONArray("tags"); + Assert.assertTrue(tags.length() > 0); + + // Deletes a tag + tagResource = resource().path("/tag/" + tag3Id); + tagResource.addFilter(new CookieAuthenticationFilter(tag1Token)); + response = tagResource.delete(ClientResponse.class); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + json = response.getEntity(JSONObject.class); + Assert.assertEquals("ok", json.getString("status")); + + // Get all tags + tagResource = resource().path("/tag/list"); + tagResource.addFilter(new CookieAuthenticationFilter(tag1Token)); + response = tagResource.get(ClientResponse.class); + Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus())); + json = response.getEntity(JSONObject.class); + tags = json.getJSONArray("tags"); + Assert.assertTrue(tags.length() == 0); + } +} \ No newline at end of file