From fe5ac0db2be035db013d6b2b6ab90e4ff30da821 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Mon, 18 Sep 2023 16:32:49 -0400 Subject: [PATCH] feat(Run): Allow teachers to archive runs (#225) --- .../HibernateAclTargetObjectIdentityDao.java | 34 +- .../wise/portal/dao/usertags/UserTagsDao.java | 16 + .../usertags/impl/HibernateUserTagsDao.java | 83 +++ .../MutableAclTargetObjectIdentity.java | 5 + .../impl/PersistentAclTargetObject.java | 3 +- .../PersistentAclTargetObjectIdentity.java | 33 +- .../wise/portal/domain/project/Project.java | 8 +- .../domain/project/impl/ProjectImpl.java | 15 +- .../wise/portal/domain/usertag/UserTag.java | 13 + .../domain/usertag/impl/UserTagImpl.java | 58 ++ .../archive/ArchiveProjectController.java | 121 ++++ .../project/ProjectAPIController.java | 4 +- .../teacher/TeacherAPIController.java | 18 +- .../web/response/ResponseEntityGenerator.java | 6 + .../service/acl/impl/AclServiceImpl.java | 8 +- .../service/project/ProjectService.java | 659 +++++++++--------- .../project/impl/ProjectServiceImpl.java | 2 +- .../service/usertags/UserTagsService.java | 22 + .../usertags/impl/UserTagsServiceImpl.java | 74 ++ src/main/resources/wise_db_init.sql | 18 +- 20 files changed, 826 insertions(+), 374 deletions(-) create mode 100644 src/main/java/org/wise/portal/dao/usertags/UserTagsDao.java create mode 100644 src/main/java/org/wise/portal/dao/usertags/impl/HibernateUserTagsDao.java create mode 100644 src/main/java/org/wise/portal/domain/usertag/UserTag.java create mode 100644 src/main/java/org/wise/portal/domain/usertag/impl/UserTagImpl.java create mode 100644 src/main/java/org/wise/portal/presentation/web/controllers/archive/ArchiveProjectController.java create mode 100644 src/main/java/org/wise/portal/service/usertags/UserTagsService.java create mode 100644 src/main/java/org/wise/portal/service/usertags/impl/UserTagsServiceImpl.java diff --git a/src/main/java/org/wise/portal/dao/authentication/impl/HibernateAclTargetObjectIdentityDao.java b/src/main/java/org/wise/portal/dao/authentication/impl/HibernateAclTargetObjectIdentityDao.java index 7646f3a727..be8d4d8f7d 100644 --- a/src/main/java/org/wise/portal/dao/authentication/impl/HibernateAclTargetObjectIdentityDao.java +++ b/src/main/java/org/wise/portal/dao/authentication/impl/HibernateAclTargetObjectIdentityDao.java @@ -20,12 +20,15 @@ */ package org.wise.portal.dao.authentication.impl; +import java.util.ArrayList; +import java.util.List; + import javax.persistence.EntityManager; -import javax.persistence.NoResultException; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.hibernate.Session; @@ -44,9 +47,9 @@ * @author Cynick Young */ @Repository -public class HibernateAclTargetObjectIdentityDao extends - AbstractHibernateDao implements - AclTargetObjectIdentityDao { +public class HibernateAclTargetObjectIdentityDao + extends AbstractHibernateDao + implements AclTargetObjectIdentityDao { @PersistenceContext private EntityManager entityManager; @@ -56,20 +59,23 @@ public class HibernateAclTargetObjectIdentityDao extends public MutableAclTargetObjectIdentity retrieveByObjectIdentity(ObjectIdentity objectIdentity) { Session session = this.getHibernateTemplate().getSessionFactory().getCurrentSession(); CriteriaBuilder cb = session.getCriteriaBuilder(); - CriteriaQuery cq = - cb.createQuery(PersistentAclTargetObjectIdentity.class); - Root persistentAclTargetObjectIdentityRoot = - cq.from(PersistentAclTargetObjectIdentity.class); - cq.select(persistentAclTargetObjectIdentityRoot).where( - cb.equal(persistentAclTargetObjectIdentityRoot.get("classname"), objectIdentity.getType())); - cq.select(persistentAclTargetObjectIdentityRoot).where( - cb.equal(persistentAclTargetObjectIdentityRoot.get("id"), objectIdentity.getIdentifier())); + CriteriaQuery cq = cb + .createQuery(PersistentAclTargetObjectIdentity.class); + Root persistentAclTargetObjectIdentityRoot = cq + .from(PersistentAclTargetObjectIdentity.class); + List predicates = new ArrayList<>(); + predicates.add(cb.equal(persistentAclTargetObjectIdentityRoot.get("aclTargetObjectId"), + objectIdentity.getIdentifier())); + predicates + .add(cb.equal(persistentAclTargetObjectIdentityRoot.get("aclTargetObject").get("classname"), + objectIdentity.getType())); + cq.select(persistentAclTargetObjectIdentityRoot) + .where(predicates.toArray(new Predicate[predicates.size()])); TypedQuery query = entityManager.createQuery(cq); return query.getResultStream().findFirst().orElse(null); } - public MutableAclTargetObjectIdentity[] findChildren( - ObjectIdentity parentIdentity) { + public MutableAclTargetObjectIdentity[] findChildren(ObjectIdentity parentIdentity) { throw new UnsupportedOperationException(); // TODO CY - not really sure what the requirements are for this method // List list = this diff --git a/src/main/java/org/wise/portal/dao/usertags/UserTagsDao.java b/src/main/java/org/wise/portal/dao/usertags/UserTagsDao.java new file mode 100644 index 0000000000..f3090cc5fc --- /dev/null +++ b/src/main/java/org/wise/portal/dao/usertags/UserTagsDao.java @@ -0,0 +1,16 @@ +package org.wise.portal.dao.usertags; + +import java.util.List; + +import org.wise.portal.dao.SimpleDao; +import org.wise.portal.domain.user.User; +import org.wise.portal.domain.usertag.UserTag; + +public interface UserTagsDao extends SimpleDao { + + UserTag get(Long tagId); + + List get(User user); + + UserTag get(User user, String text); +} diff --git a/src/main/java/org/wise/portal/dao/usertags/impl/HibernateUserTagsDao.java b/src/main/java/org/wise/portal/dao/usertags/impl/HibernateUserTagsDao.java new file mode 100644 index 0000000000..f22b3d44d4 --- /dev/null +++ b/src/main/java/org/wise/portal/dao/usertags/impl/HibernateUserTagsDao.java @@ -0,0 +1,83 @@ +package org.wise.portal.dao.usertags.impl; + +import java.util.ArrayList; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + +import org.hibernate.Session; +import org.springframework.stereotype.Repository; +import org.wise.portal.dao.impl.AbstractHibernateDao; +import org.wise.portal.dao.usertags.UserTagsDao; +import org.wise.portal.domain.user.User; +import org.wise.portal.domain.usertag.UserTag; +import org.wise.portal.domain.usertag.impl.UserTagImpl; + +@Repository +public class HibernateUserTagsDao extends AbstractHibernateDao + implements UserTagsDao { + + @PersistenceContext + private EntityManager entityManager; + + private static final String FIND_ALL_QUERY = "from TagsImpl"; + + @Override + protected String getFindAllQuery() { + return FIND_ALL_QUERY; + } + + @Override + protected Class getDataObjectClass() { + return UserTag.class; + } + + public UserTag get(Long tagId) { + CriteriaBuilder cb = getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(UserTagImpl.class); + Root tagsRoot = cq.from(UserTagImpl.class); + List predicates = new ArrayList<>(); + predicates.add(cb.equal(tagsRoot.get("id"), tagId)); + cq.select(tagsRoot).where(predicates.toArray(new Predicate[predicates.size()])); + TypedQuery query = entityManager.createQuery(cq); + List tagsResultList = query.getResultList(); + return tagsResultList.isEmpty() ? null : tagsResultList.get(0); + } + + @SuppressWarnings("unchecked") + public List get(User user) { + CriteriaBuilder cb = getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(UserTagImpl.class); + Root tagsRoot = cq.from(UserTagImpl.class); + List predicates = new ArrayList<>(); + predicates.add(cb.equal(tagsRoot.get("user").get("id"), user.getId())); + cq.select(tagsRoot).where(predicates.toArray(new Predicate[predicates.size()])); + TypedQuery query = entityManager.createQuery(cq); + List userTagsResult = query.getResultList(); + return (List) (Object) userTagsResult; + } + + public UserTag get(User user, String text) { + CriteriaBuilder cb = getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(UserTagImpl.class); + Root tagsRoot = cq.from(UserTagImpl.class); + List predicates = new ArrayList<>(); + predicates.add(cb.equal(tagsRoot.get("user").get("id"), user.getId())); + predicates.add(cb.equal(cb.lower(tagsRoot.get("text")), text.toLowerCase())); + cq.select(tagsRoot).where(predicates.toArray(new Predicate[predicates.size()])); + TypedQuery query = entityManager.createQuery(cq); + List tagsResultList = query.getResultList(); + return tagsResultList.isEmpty() ? null : tagsResultList.get(0); + } + + private CriteriaBuilder getCriteriaBuilder() { + Session session = this.getHibernateTemplate().getSessionFactory().getCurrentSession(); + return session.getCriteriaBuilder(); + } +} diff --git a/src/main/java/org/wise/portal/domain/authentication/MutableAclTargetObjectIdentity.java b/src/main/java/org/wise/portal/domain/authentication/MutableAclTargetObjectIdentity.java index 1360f3af36..130f64fabb 100644 --- a/src/main/java/org/wise/portal/domain/authentication/MutableAclTargetObjectIdentity.java +++ b/src/main/java/org/wise/portal/domain/authentication/MutableAclTargetObjectIdentity.java @@ -20,8 +20,11 @@ */ package org.wise.portal.domain.authentication; +import java.util.Set; + import org.springframework.security.acls.model.ObjectIdentity; import org.wise.portal.domain.Persistable; +import org.wise.portal.domain.usertag.UserTag; /** * Mutable extension of ObjectIdentity. Represents the object @@ -83,4 +86,6 @@ public interface MutableAclTargetObjectIdentity extends ObjectIdentity, Persista * @param parent the parent to set */ void setParent(MutableAclTargetObjectIdentity parent); + + Set getTags(); } diff --git a/src/main/java/org/wise/portal/domain/authentication/impl/PersistentAclTargetObject.java b/src/main/java/org/wise/portal/domain/authentication/impl/PersistentAclTargetObject.java index cbb71bc532..bf482e9004 100644 --- a/src/main/java/org/wise/portal/domain/authentication/impl/PersistentAclTargetObject.java +++ b/src/main/java/org/wise/portal/domain/authentication/impl/PersistentAclTargetObject.java @@ -73,8 +73,7 @@ public class PersistentAclTargetObject implements MutableAclTargetObject { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result - + ((classname == null) ? 0 : classname.hashCode()); + result = prime * result + ((classname == null) ? 0 : classname.hashCode()); return result; } diff --git a/src/main/java/org/wise/portal/domain/authentication/impl/PersistentAclTargetObjectIdentity.java b/src/main/java/org/wise/portal/domain/authentication/impl/PersistentAclTargetObjectIdentity.java index f82d7d62fa..11b2a2d731 100644 --- a/src/main/java/org/wise/portal/domain/authentication/impl/PersistentAclTargetObjectIdentity.java +++ b/src/main/java/org/wise/portal/domain/authentication/impl/PersistentAclTargetObjectIdentity.java @@ -21,14 +21,19 @@ package org.wise.portal.domain.authentication.impl; import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; @@ -41,6 +46,8 @@ import org.wise.portal.domain.authentication.MutableAclSid; import org.wise.portal.domain.authentication.MutableAclTargetObject; import org.wise.portal.domain.authentication.MutableAclTargetObjectIdentity; +import org.wise.portal.domain.usertag.UserTag; +import org.wise.portal.domain.usertag.impl.UserTagImpl; /** * Concrete implementation of MutableAclTargetObjectIdentity @@ -50,9 +57,9 @@ * @see org.springframework.security.acls.model.ObjectIdentity */ @Entity -@Table(name = PersistentAclTargetObjectIdentity.DATA_STORE_NAME, uniqueConstraints = { @UniqueConstraint(columnNames = { - PersistentAclTargetObjectIdentity.COLUMN_NAME_TARGET_OBJECT, - PersistentAclTargetObjectIdentity.COLUMN_NAME_TARGET_OBJECT_ID }) }) +@Table(name = PersistentAclTargetObjectIdentity.DATA_STORE_NAME, uniqueConstraints = { + @UniqueConstraint(columnNames = { PersistentAclTargetObjectIdentity.COLUMN_NAME_TARGET_OBJECT, + PersistentAclTargetObjectIdentity.COLUMN_NAME_TARGET_OBJECT_ID }) }) public class PersistentAclTargetObjectIdentity implements MutableAclTargetObjectIdentity { @Transient @@ -108,6 +115,14 @@ public class PersistentAclTargetObjectIdentity implements MutableAclTargetObject @Column(name = COLUMN_NAME_INHERITING, nullable = false) private Boolean inheriting; + @ManyToMany(targetEntity = UserTagImpl.class, fetch = FetchType.LAZY) + @JoinTable(name = "acl_object_identity_to_user_tags", joinColumns = { + @JoinColumn(name = "acl_object_identity_fk", nullable = false) }, inverseJoinColumns = { + @JoinColumn(name = "user_tags_fk", nullable = false) }) + @Getter + @Setter + private Set tags = new HashSet(); + @Id @GeneratedValue(strategy = GenerationType.AUTO) @Getter @@ -116,7 +131,9 @@ public class PersistentAclTargetObjectIdentity implements MutableAclTargetObject @Version @Column(name = "OPTLOCK") - private Integer version = null; + @Getter + @Setter + private Integer version = 0; @Override public String getType() { @@ -140,12 +157,8 @@ public void setInheriting(Boolean isInheriting) { public int hashCode() { final int PRIME = 31; int result = 1; - result = PRIME * result - + ((aclTargetObject == null) ? 0 : aclTargetObject.hashCode()); - result = PRIME - * result - + ((aclTargetObjectId == null) ? 0 : aclTargetObjectId - .hashCode()); + result = PRIME * result + ((aclTargetObject == null) ? 0 : aclTargetObject.hashCode()); + result = PRIME * result + ((aclTargetObjectId == null) ? 0 : aclTargetObjectId.hashCode()); return result; } diff --git a/src/main/java/org/wise/portal/domain/project/Project.java b/src/main/java/org/wise/portal/domain/project/Project.java index 54fb571109..12cb6373f4 100644 --- a/src/main/java/org/wise/portal/domain/project/Project.java +++ b/src/main/java/org/wise/portal/domain/project/Project.java @@ -23,15 +23,15 @@ */ package org.wise.portal.domain.project; +import java.io.Serializable; +import java.util.Date; +import java.util.Set; + import org.wise.portal.domain.Persistable; import org.wise.portal.domain.Tag; import org.wise.portal.domain.project.impl.ProjectType; import org.wise.portal.domain.user.User; -import java.io.Serializable; -import java.util.Date; -import java.util.Set; - /** * A WISE Project domain object * diff --git a/src/main/java/org/wise/portal/domain/project/impl/ProjectImpl.java b/src/main/java/org/wise/portal/domain/project/impl/ProjectImpl.java index e9ef452b35..57c166cb2a 100644 --- a/src/main/java/org/wise/portal/domain/project/impl/ProjectImpl.java +++ b/src/main/java/org/wise/portal/domain/project/impl/ProjectImpl.java @@ -128,7 +128,7 @@ public class ProjectImpl implements Project { protected String name; @OneToOne(targetEntity = ProjectMetadataImpl.class, fetch = FetchType.LAZY) - @Cascade( { org.hibernate.annotations.CascadeType.SAVE_UPDATE }) + @Cascade({ org.hibernate.annotations.CascadeType.SAVE_UPDATE }) @JoinColumn(name = "metadata_fk", nullable = true, unique = true) protected ProjectMetadata metadataObj = null; @@ -147,13 +147,15 @@ public class ProjectImpl implements Project { private User owner; @ManyToMany(targetEntity = UserImpl.class, fetch = FetchType.LAZY) - @JoinTable(name = SHARED_OWNERS_JOIN_TABLE_NAME, joinColumns = { @JoinColumn(name = PROJECTS_JOIN_COLUMN_NAME, nullable = false) }, inverseJoinColumns = @JoinColumn(name = SHARED_OWNERS_JOIN_COLUMN_NAME, nullable = false)) + @JoinTable(name = SHARED_OWNERS_JOIN_TABLE_NAME, joinColumns = { + @JoinColumn(name = PROJECTS_JOIN_COLUMN_NAME, nullable = false) }, inverseJoinColumns = @JoinColumn(name = SHARED_OWNERS_JOIN_COLUMN_NAME, nullable = false)) @Getter @Setter private Set sharedowners = new TreeSet(); @ManyToMany(targetEntity = UserImpl.class, fetch = FetchType.LAZY) - @JoinTable(name = BOOKMARKERS_JOIN_TABLE_NAME, joinColumns = { @JoinColumn(name= PROJECTS_JOIN_COLUMN_NAME, nullable = false) }, inverseJoinColumns = @JoinColumn(name = BOOKMARKERS_JOIN_COLUMN_NAME, nullable = false)) + @JoinTable(name = BOOKMARKERS_JOIN_TABLE_NAME, joinColumns = { + @JoinColumn(name = PROJECTS_JOIN_COLUMN_NAME, nullable = false) }, inverseJoinColumns = @JoinColumn(name = BOOKMARKERS_JOIN_COLUMN_NAME, nullable = false)) @Getter @Setter private Set bookmarkers = new TreeSet(); @@ -204,7 +206,8 @@ public class ProjectImpl implements Project { protected Date dateCreated; @ManyToMany(targetEntity = TagImpl.class, fetch = FetchType.LAZY) - @JoinTable(name = TAGS_JOIN_TABLE_NAME, joinColumns = { @JoinColumn(name = PROJECT_JOIN_COLUMN_NAME, nullable = false) }, inverseJoinColumns = @JoinColumn(name = TAGS_JOIN_COLUMN_NAME, nullable = false)) + @JoinTable(name = TAGS_JOIN_TABLE_NAME, joinColumns = { + @JoinColumn(name = PROJECT_JOIN_COLUMN_NAME, nullable = false) }, inverseJoinColumns = @JoinColumn(name = TAGS_JOIN_COLUMN_NAME, nullable = false)) @Getter @Setter protected Set tags = new TreeSet(); @@ -289,7 +292,7 @@ public String getName() { return name; } - public void populateProjectInfo(){ + public void populateProjectInfo() { this.projectinfo = new ProjectInfoImpl(); this.projectinfo.setName(this.getName()); } @@ -410,7 +413,7 @@ public boolean isCommunityProject() { public boolean hasTag(String tag) { Set projectTags = this.getTags(); - for (Tag projectTag: projectTags) { + for (Tag projectTag : projectTags) { if (projectTag.getName().equals(tag)) { return true; } diff --git a/src/main/java/org/wise/portal/domain/usertag/UserTag.java b/src/main/java/org/wise/portal/domain/usertag/UserTag.java new file mode 100644 index 0000000000..e5591de66f --- /dev/null +++ b/src/main/java/org/wise/portal/domain/usertag/UserTag.java @@ -0,0 +1,13 @@ +package org.wise.portal.domain.usertag; + +import org.wise.portal.domain.Persistable; +import org.wise.portal.domain.user.User; + +public interface UserTag extends Persistable { + + String getText(); + + void setText(String text); + + User getUser(); +} \ No newline at end of file diff --git a/src/main/java/org/wise/portal/domain/usertag/impl/UserTagImpl.java b/src/main/java/org/wise/portal/domain/usertag/impl/UserTagImpl.java new file mode 100644 index 0000000000..535f5e5eb5 --- /dev/null +++ b/src/main/java/org/wise/portal/domain/usertag/impl/UserTagImpl.java @@ -0,0 +1,58 @@ +package org.wise.portal.domain.usertag.impl; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Transient; + +import org.wise.portal.domain.user.User; +import org.wise.portal.domain.user.impl.UserImpl; +import org.wise.portal.domain.usertag.UserTag; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import lombok.Getter; +import lombok.Setter; + +@Entity +@Table(name = UserTagImpl.DATA_STORE_NAME) +public class UserTagImpl implements UserTag { + + @Transient + public static final long serialVersionUID = 1L; + + @Transient + public static final String DATA_STORE_NAME = "user_tags"; + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Getter + @Setter + private Long id = null; + + @ManyToOne(targetEntity = UserImpl.class, fetch = FetchType.LAZY) + @JoinColumn(name = "users_fk", nullable = false) + @Getter + @Setter + @JsonIgnore + private User user; + + @Getter + @Setter + @Column(name = "text") + private String text; + + public UserTagImpl() { + } + + public UserTagImpl(User user, String text) { + this.user = user; + this.text = text; + } +} \ No newline at end of file diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/archive/ArchiveProjectController.java b/src/main/java/org/wise/portal/presentation/web/controllers/archive/ArchiveProjectController.java new file mode 100644 index 0000000000..43d9ceeb0f --- /dev/null +++ b/src/main/java/org/wise/portal/presentation/web/controllers/archive/ArchiveProjectController.java @@ -0,0 +1,121 @@ +package org.wise.portal.presentation.web.controllers.archive; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.wise.portal.dao.ObjectNotFoundException; +import org.wise.portal.domain.project.Project; +import org.wise.portal.domain.project.impl.ProjectImpl; +import org.wise.portal.domain.user.User; +import org.wise.portal.domain.usertag.UserTag; +import org.wise.portal.presentation.web.response.ResponseEntityGenerator; +import org.wise.portal.service.project.ProjectService; +import org.wise.portal.service.user.UserService; +import org.wise.portal.service.usertags.UserTagsService; + +@RestController +@Secured({ "ROLE_TEACHER" }) +@RequestMapping("/api") +public class ArchiveProjectController { + + @Autowired + private ProjectService projectService; + + @Autowired + private UserService userService; + + @Autowired + private UserTagsService userTagsService; + + private static final String ARCHIVED_TAG = "archived"; + + @PutMapping("/project/{projectId}/archived") + protected ResponseEntity> archiveProject(Authentication auth, + @PathVariable("projectId") ProjectImpl project) { + User user = userService.retrieveUserByUsername(auth.getName()); + UserTag archivedTag = getOrCreateArchivedTag(user); + userTagsService.applyTag(project, archivedTag); + return ResponseEntityGenerator.createSuccess(createProjectResponse(user, project)); + } + + @PutMapping("/projects/archived") + protected ResponseEntity>> archiveProjects(Authentication auth, + @RequestBody List projectIds) throws Exception { + User user = userService.retrieveUserByUsername(auth.getName()); + UserTag archivedTag = getOrCreateArchivedTag(user); + List projects = getProjects(projectIds); + for (Project project : projects) { + userTagsService.applyTag(project, archivedTag); + } + return ResponseEntityGenerator.createSuccess(createProjectsResponse(user, projects)); + } + + private UserTag getOrCreateArchivedTag(User user) { + UserTag archivedTag = userTagsService.get(user, ARCHIVED_TAG); + if (archivedTag == null) { + archivedTag = userTagsService.createTag(user, ARCHIVED_TAG); + } + return archivedTag; + } + + @DeleteMapping("/project/{projectId}/archived") + protected ResponseEntity> unarchiveProject(Authentication auth, + @PathVariable("projectId") ProjectImpl project) { + User user = userService.retrieveUserByUsername(auth.getName()); + UserTag archivedTag = userTagsService.get(user, ARCHIVED_TAG); + if (archivedTag != null) { + userTagsService.removeTag(project, archivedTag); + } + return ResponseEntityGenerator.createSuccess(createProjectResponse(user, project)); + } + + @DeleteMapping("/projects/archived") + protected ResponseEntity>> unarchiveProjects(Authentication auth, + @RequestParam List projectIds) throws Exception { + User user = userService.retrieveUserByUsername(auth.getName()); + UserTag archivedTag = userTagsService.get(user, ARCHIVED_TAG); + List projects = getProjects(projectIds); + if (archivedTag != null) { + for (Project project : projects) { + userTagsService.removeTag(project, archivedTag); + } + } + return ResponseEntityGenerator.createSuccess(createProjectsResponse(user, projects)); + } + + private List getProjects(List projectIds) throws ObjectNotFoundException { + List projects = new ArrayList(); + for (Long projectId : projectIds) { + projects.add(projectService.getById(projectId)); + } + return projects; + } + + private Map createProjectResponse(User user, Project project) { + Map response = new HashMap(); + response.put("id", project.getId()); + response.put("archived", userTagsService.hasTag(user, project, ARCHIVED_TAG)); + return response; + } + + private List> createProjectsResponse(User user, List projects) { + List> response = new ArrayList>(); + for (Project project : projects) { + response.add(createProjectResponse(user, project)); + } + return response; + } +} diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/project/ProjectAPIController.java b/src/main/java/org/wise/portal/presentation/web/controllers/project/ProjectAPIController.java index fef57d0eb1..d5b9d6e5de 100644 --- a/src/main/java/org/wise/portal/presentation/web/controllers/project/ProjectAPIController.java +++ b/src/main/java/org/wise/portal/presentation/web/controllers/project/ProjectAPIController.java @@ -41,8 +41,8 @@ public class ProjectAPIController { ProjectService projectService; @GetMapping("/library") - protected String getLibraryProjects(ModelMap modelMap) throws ObjectNotFoundException, - JSONException { + protected String getLibraryProjects(ModelMap modelMap) + throws ObjectNotFoundException, JSONException { Portal portal = portalService.getById(new Integer(1)); String projectLibraryGroups = portal.getProjectLibraryGroups(); JSONArray projectLibraryGroupsJSON = new JSONArray(projectLibraryGroups); diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java b/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java index e128b21d5b..d93d9ae0c2 100644 --- a/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java +++ b/src/main/java/org/wise/portal/presentation/web/controllers/teacher/TeacherAPIController.java @@ -1,12 +1,14 @@ package org.wise.portal.presentation.web.controllers.teacher; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.stream.Collectors; import javax.mail.MessagingException; import javax.servlet.http.HttpServletRequest; @@ -32,6 +34,7 @@ import org.wise.portal.domain.group.Group; import org.wise.portal.domain.run.Run; import org.wise.portal.domain.user.User; +import org.wise.portal.domain.usertag.UserTag; import org.wise.portal.presentation.web.controllers.ControllerUtil; import org.wise.portal.presentation.web.controllers.user.UserAPIController; import org.wise.portal.presentation.web.exception.InvalidNameException; @@ -39,6 +42,7 @@ import org.wise.portal.presentation.web.response.SimpleResponse; import org.wise.portal.service.authentication.DuplicateUsernameException; import org.wise.portal.service.authentication.UserDetailsService; +import org.wise.portal.service.usertags.UserTagsService; /** * Teacher REST API @@ -55,6 +59,9 @@ public class TeacherAPIController extends UserAPIController { @Autowired private UserDetailsService userDetailsService; + @Autowired + private UserTagsService userTagsService; + @Value("${google.clientId:}") private String googleClientId; @@ -77,11 +84,20 @@ List> getRuns(Authentication auth, private List> getRunsList(User user, List runs) { List> runsList = new ArrayList>(); for (Run run : runs) { - runsList.add(getRunMap(user, run)); + HashMap runMap = getRunMap(user, run); + Set tags = userTagsService.getTags(user, run.getProject()); + ((HashMap) runMap.get("project")).put("tags", getTagsList(tags)); + runsList.add(runMap); } return runsList; } + private List getTagsList(Set tags) { + List tagsList = tags.stream().map(tag -> tag.getText()).collect(Collectors.toList()); + Collections.sort(tagsList); + return tagsList; + } + @GetMapping("/run/{runId}") HashMap getRun(Authentication auth, @PathVariable Long runId) throws ObjectNotFoundException { diff --git a/src/main/java/org/wise/portal/presentation/web/response/ResponseEntityGenerator.java b/src/main/java/org/wise/portal/presentation/web/response/ResponseEntityGenerator.java index dfff3f46e9..c6d4c38288 100644 --- a/src/main/java/org/wise/portal/presentation/web/response/ResponseEntityGenerator.java +++ b/src/main/java/org/wise/portal/presentation/web/response/ResponseEntityGenerator.java @@ -1,6 +1,7 @@ package org.wise.portal.presentation.web.response; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.springframework.http.HttpStatus; @@ -17,6 +18,11 @@ public static ResponseEntity> createSuccess(Map(map, HttpStatus.OK); } + public static ResponseEntity>> createSuccess( + List> list) { + return new ResponseEntity<>(list, HttpStatus.OK); + } + public static ResponseEntity> createError(String messageCode) { Map body = new HashMap<>(); body.put("messageCode", messageCode); diff --git a/src/main/java/org/wise/portal/service/acl/impl/AclServiceImpl.java b/src/main/java/org/wise/portal/service/acl/impl/AclServiceImpl.java index 90ea0509e4..f3ce90c16c 100644 --- a/src/main/java/org/wise/portal/service/acl/impl/AclServiceImpl.java +++ b/src/main/java/org/wise/portal/service/acl/impl/AclServiceImpl.java @@ -178,8 +178,8 @@ public boolean hasSpecificPermission(T object, Permission permission, UserDetail @SuppressWarnings("unchecked") @Override - public boolean hasPermission( - Authentication authentication, Object targetDomainObject, Object permission) { + public boolean hasPermission(Authentication authentication, Object targetDomainObject, + Object permission) { Collection grantedAuthorities = authentication.getAuthorities(); if (grantedAuthorities.size() == 1) { if ("[ROLE_ANONYMOUS]".equals(grantedAuthorities.toString())) { @@ -230,8 +230,8 @@ private Permission getBasePermission(String permissionStr) { } @Override - public boolean hasPermission( - Authentication authentication, Serializable targetId, String targetType, Object permission) { + public boolean hasPermission(Authentication authentication, Serializable targetId, + String targetType, Object permission) { // TODO Auto-generated method stub return false; } diff --git a/src/main/java/org/wise/portal/service/project/ProjectService.java b/src/main/java/org/wise/portal/service/project/ProjectService.java index 7e905474cf..8aef8f313e 100644 --- a/src/main/java/org/wise/portal/service/project/ProjectService.java +++ b/src/main/java/org/wise/portal/service/project/ProjectService.java @@ -23,6 +23,15 @@ */ package org.wise.portal.service.project; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.json.JSONException; import org.json.JSONObject; import org.springframework.security.access.annotation.Secured; @@ -42,15 +51,6 @@ import org.wise.portal.presentation.web.exception.TeacherAlreadySharedWithProjectException; import org.wise.portal.presentation.web.response.SharedOwner; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.Serializable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - /** * A Service for Projects * @@ -58,338 +58,339 @@ */ public interface ProjectService { - void init(); - - /** - * Get a List of Project that the specified user owns. - * - * @return a List of Project - */ - @Transactional - @Secured({ "ROLE_USER", "AFTER_ACL_COLLECTION_READ" }) - List getProjectList(User user); - - /** - * Get a List of Project that have been shared with the specified user. - * - * @return a List of Project - */ - @Transactional - @Secured({ "ROLE_USER", "AFTER_ACL_COLLECTION_READ" }) - List getSharedProjectList(User user); - - @Transactional - @Secured({ "ROLE_USER", "AFTER_ACL_COLLECTION_READ" }) - List getSharedProjectsWithoutRun(User user); - - /** - * Retrieves a List of Project that has been bookmarked by the given - * User. - * - * @param bookmarker User who we're looking up - * @return List - * @throws ObjectNotFoundException - */ - @Transactional - List getBookmarkerProjectList(User bookmarker) throws ObjectNotFoundException; - - /** - * Adds the given User bookmarker to the Project project. - * - * @param project the project to bookmark - * @param bookmarker User that wants to bookmark the project - */ - @Transactional - void addBookmarkerToProject(Project project, User bookmarker) throws ObjectNotFoundException; - - /** - * Removes the given User bookmarker from the Project project. - * - * @param project Project - * @param bookmarker User - */ - @Transactional - void removeBookmarkerFromProject(Project project, User bookmarker) throws ObjectNotFoundException; - - /** - * Returns the permission that the specified user has on the specified project. - * - * @param project The Project that is shared. - * @param user The User that shares the Project - * @return A String containing the permission that the user has on the project. If - * the user does not have permission on the project, null is returned. - */ - @Transactional(readOnly = true) - String getSharedTeacherRole(Project project, User user); - - /** - * Add shared teacher to a project. - * - * @param addSharedTeacherParameters - */ - @Secured({ "ROLE_TEACHER" }) - @Transactional() - void addSharedTeacherToProject(AddSharedTeacherParameters addSharedTeacherParameters) - throws ObjectNotFoundException; - - @Secured({ "ROLE_TEACHER" }) - @Transactional() - void removeSharedTeacherFromProject(Project project, User user) throws ObjectNotFoundException; - - /** - * Creates a new Project. - * - * @param projectParameters ProjectParameters the project parameters object - * @return the Project that was created - * @throws ObjectNotFoundException when projectparameters references objects that do not exist - * in the datastore - */ - Project createProject(ProjectParameters projectParameters) throws ObjectNotFoundException; - - /** - * Saves the project - * - * @param project Project contains updated Project. - */ - @Transactional() - void updateProject(Project project, User user) throws NotAuthorizedException; - - /** - * Launches the VLE for the specified Workgroup. - * - * @param workgroup Workgroup requesting to launch the project - * @return - * @throws Exception - */ - ModelAndView launchProject(Workgroup workgroup, String contextPath) throws Exception; - - String generateStudentStartProjectUrlString(Workgroup workgroup, String contextPath); - - /** - * Launches a Preview of the Project. - * - * @param previewProjectParameters parameters required to preview the project - * @throws ObjectNotFoundException when the specified projectId does not exist - * @throws IOException when the url cannot be loaded - */ - Object previewProject(PreviewProjectParameters previewProjectParameters) throws Exception; - - /** - * Gets a project with the given projectId. - * - * @param projectId the id of the project - * @return Project with the specified projectId - * @throws ObjectNotFoundException when the specified projectId does not exist - */ - Project getById(Serializable projectId) throws ObjectNotFoundException; - - /** - * Given a Project project and User user, returns boolean - * true if the user is allowed to create a run from that project (ie, project is TELS, owner, - * sharedOwner), returns false otherwise. - * - * @param project - * @param user - * @return boolean - */ - boolean canCreateRun(Project project, User user); - - /** - * Given a Project project and User user, returns true if the user is - * allowed to author that particular project, returns false otherwise. - * - * @param project - * @param user - * @return boolean - */ - boolean canAuthorProject(Project project, User user); - - /** - * Given a Project project and a User user, returns true if the user has - * read access to that particular project, returns false otherwise. - * - * @param project - * @param user - * @return - */ - boolean canReadProject(Project project, User user); - - /** - * Returns a List list of all projects in the data store. - * - * @return List projects - */ - List getAdminProjectList(); - - /** - * Returns a List list of library projects Library projects show up in - * "Browse Library" page but not on the homepage. Library projects show up in both "Browse - * Library" page and in the homepage. - * - * @return List - list of library projects - */ - List getPublicLibraryProjectList(); - - List getTeacherSharedProjectList(); - - /** - * Returns a List list of library projects. Library projects show up in - * "Browse Library" page but not on the homepage. Library projects show up in both "Browse - * Library" page and in the homepage. - * - * @return List - list of library projects - */ - List getLibraryProjectList(); - - /** - * Given a Set set of tag names, returns a List list of - * projects with all of the tag names. - * - * @param tagNames Set - set of tagNames - * @return List - list of projects - */ - List getProjectListByTagNames(Set tagNames); - - /** - * Given a partial author name (e.g. "hiro", "hiroki"), returns a list of projects that were - * authored by that person. - * - * @param authorName partial or full author name - * @return List - list of projects - */ - List getProjectListByAuthorName(String authorName); - - /** - * Given a partial title (e.g. "Global", "Global Climate"), returns a list of projects that match - * that title. - * - * @param title partial or full project title - * @return List - list of projects - */ - List getProjectListByTitle(String title); - - /** - * Given a String and a Project adds the tag to the project. - * - * @param tag - * @param projectId - */ - @Transactional - Integer addTagToProject(String tag, Long projectId); - - /** - * Given a Tag and a Project, removes the tag from the project. - * - * @param tagId Integer id of tag - * @param projectId id of project - */ - @Transactional - void removeTagFromProject(Integer tagId, Long projectId); - - /** - * Given a Long tag id, a project id and a name, updates that project tag to that - * name, returning the resulting tag Id. - * - * @param tagId Integer id of tag - * @param projectId id of project - * @param name name of tag - * @return Integer - tag id - */ - Integer updateTag(Integer tagId, Long projectId, String name); - - /** - * Given a Project and a String tag name, returns boolean true if the - * project contains a tag with that name, false otherwise. - * - * @param project - * @param name name of tag - * @return boolean - */ - boolean projectContainsTag(Project project, String name); - - /** - * Given a User user and a String tag name, returns true if that user is - * authorized to create a tag with that name, returns false otherwise. - * - * @param user - * @param name - * @return boolean - */ - boolean isAuthorizedToCreateTag(User user, String name); - - /** - * Given a project id, returns projects that are copies of the project. - * - * @param projectId - * @return - */ - List getProjectCopies(Long projectId); - - /** - * Given a project, gets the project id for the project's root level project. - * - * @param project - * @return id of the root project - * @throws ObjectNotFoundException - */ - Long identifyRootProjectId(Project project) throws ObjectNotFoundException; - - long getNextAvailableProjectId(); - - Project copyProject(Long projectId, User user) throws Exception; - - void saveProjectContentToDisk(String projectJSONString, Project project) - throws FileNotFoundException, IOException; - - List getSharedTeacherPermissions(Project project, User sharedTeacher); - - SharedOwner addSharedTeacher(Long projectId, String username) - throws ObjectNotFoundException, TeacherAlreadySharedWithProjectException; - - void removeSharedTeacher(Long projectId, String username) throws ObjectNotFoundException; - - void addSharedTeacherPermission(Long projectId, Long userId, Integer permissionId) - throws ObjectNotFoundException; - - void removeSharedTeacherPermission(Long projectId, Long userId, Integer permissionId) - throws ObjectNotFoundException; - - void transferProjectOwnership(Project project, User newOwner) throws ObjectNotFoundException; - - List getProjectsWithoutRuns(User user); + void init(); + + /** + * Get a List of Project that the specified user owns. + * + * @return a List of Project + */ + @Transactional + @Secured({ "ROLE_USER", "AFTER_ACL_COLLECTION_READ" }) + List getProjectList(User user); + + /** + * Get a List of Project that have been shared with the specified user. + * + * @return a List of Project + */ + @Transactional + @Secured({ "ROLE_USER", "AFTER_ACL_COLLECTION_READ" }) + List getSharedProjectList(User user); + + @Transactional + @Secured({ "ROLE_USER", "AFTER_ACL_COLLECTION_READ" }) + List getSharedProjectsWithoutRun(User user); + + /** + * Retrieves a List of Project that has been bookmarked by the given + * User. + * + * @param bookmarker User who we're looking up + * @return List + * @throws ObjectNotFoundException + */ + @Transactional + List getBookmarkerProjectList(User bookmarker) throws ObjectNotFoundException; + + /** + * Adds the given User bookmarker to the Project project. + * + * @param project the project to bookmark + * @param bookmarker User that wants to bookmark the project + */ + @Transactional + void addBookmarkerToProject(Project project, User bookmarker) throws ObjectNotFoundException; + + /** + * Removes the given User bookmarker from the Project project. + * + * @param project Project + * @param bookmarker User + */ + @Transactional + void removeBookmarkerFromProject(Project project, User bookmarker) + throws ObjectNotFoundException; + + /** + * Returns the permission that the specified user has on the specified project. + * + * @param project The Project that is shared. + * @param user The User that shares the Project + * @return A String containing the permission that the user has on the project. If + * the user does not have permission on the project, null is returned. + */ + @Transactional(readOnly = true) + String getSharedTeacherRole(Project project, User user); + + /** + * Add shared teacher to a project. + * + * @param addSharedTeacherParameters + */ + @Secured({ "ROLE_TEACHER" }) + @Transactional() + void addSharedTeacherToProject(AddSharedTeacherParameters addSharedTeacherParameters) + throws ObjectNotFoundException; + + @Secured({ "ROLE_TEACHER" }) + @Transactional() + void removeSharedTeacherFromProject(Project project, User user) throws ObjectNotFoundException; + + /** + * Creates a new Project. + * + * @param projectParameters ProjectParameters the project parameters object + * @return the Project that was created + * @throws ObjectNotFoundException when projectparameters references objects that do not exist + * in the datastore + */ + Project createProject(ProjectParameters projectParameters) throws ObjectNotFoundException; + + /** + * Saves the project + * + * @param project Project contains updated Project. + */ + @Transactional() + void updateProject(Project project, User user) throws NotAuthorizedException; + + /** + * Launches the VLE for the specified Workgroup. + * + * @param workgroup Workgroup requesting to launch the project + * @return + * @throws Exception + */ + ModelAndView launchProject(Workgroup workgroup, String contextPath) throws Exception; + + String generateStudentStartProjectUrlString(Workgroup workgroup, String contextPath); + + /** + * Launches a Preview of the Project. + * + * @param previewProjectParameters parameters required to preview the project + * @throws ObjectNotFoundException when the specified projectId does not exist + * @throws IOException when the url cannot be loaded + */ + Object previewProject(PreviewProjectParameters previewProjectParameters) throws Exception; + + /** + * Gets a project with the given projectId. + * + * @param projectId the id of the project + * @return Project with the specified projectId + * @throws ObjectNotFoundException when the specified projectId does not exist + */ + Project getById(Serializable projectId) throws ObjectNotFoundException; + + /** + * Given a Project project and User user, returns boolean + * true if the user is allowed to create a run from that project (ie, project is TELS, owner, + * sharedOwner), returns false otherwise. + * + * @param project + * @param user + * @return boolean + */ + boolean canCreateRun(Project project, User user); + + /** + * Given a Project project and User user, returns true if the user is + * allowed to author that particular project, returns false otherwise. + * + * @param project + * @param user + * @return boolean + */ + boolean canAuthorProject(Project project, User user); + + /** + * Given a Project project and a User user, returns true if the user has + * read access to that particular project, returns false otherwise. + * + * @param project + * @param user + * @return + */ + boolean canReadProject(Project project, User user); + + /** + * Returns a List list of all projects in the data store. + * + * @return List projects + */ + List getAdminProjectList(); + + /** + * Returns a List list of library projects Library projects show up in + * "Browse Library" page but not on the homepage. Library projects show up in both "Browse + * Library" page and in the homepage. + * + * @return List - list of library projects + */ + List getPublicLibraryProjectList(); + + List getTeacherSharedProjectList(); + + /** + * Returns a List list of library projects. Library projects show up in + * "Browse Library" page but not on the homepage. Library projects show up in both "Browse + * Library" page and in the homepage. + * + * @return List - list of library projects + */ + List getLibraryProjectList(); + + /** + * Given a Set set of tag names, returns a List list of + * projects with all of the tag names. + * + * @param tagNames Set - set of tagNames + * @return List - list of projects + */ + List getProjectListByTagNames(Set tagNames); + + /** + * Given a partial author name (e.g. "hiro", "hiroki"), returns a list of projects that were + * authored by that person. + * + * @param authorName partial or full author name + * @return List - list of projects + */ + List getProjectListByAuthorName(String authorName); + + /** + * Given a partial title (e.g. "Global", "Global Climate"), returns a list of projects that match + * that title. + * + * @param title partial or full project title + * @return List - list of projects + */ + List getProjectListByTitle(String title); + + /** + * Given a String and a Project adds the tag to the project. + * + * @param tag + * @param projectId + */ + @Transactional + Integer addTagToProject(String tag, Long projectId); + + /** + * Given a Tag and a Project, removes the tag from the project. + * + * @param tagId Integer id of tag + * @param projectId id of project + */ + @Transactional + void removeTagFromProject(Integer tagId, Long projectId); + + /** + * Given a Long tag id, a project id and a name, updates that project tag to that + * name, returning the resulting tag Id. + * + * @param tagId Integer id of tag + * @param projectId id of project + * @param name name of tag + * @return Integer - tag id + */ + Integer updateTag(Integer tagId, Long projectId, String name); + + /** + * Given a Project and a String tag name, returns boolean true if the + * project contains a tag with that name, false otherwise. + * + * @param project + * @param name name of tag + * @return boolean + */ + boolean projectContainsTag(Project project, String name); + + /** + * Given a User user and a String tag name, returns true if that user is + * authorized to create a tag with that name, returns false otherwise. + * + * @param user + * @param name + * @return boolean + */ + boolean isAuthorizedToCreateTag(User user, String name); + + /** + * Given a project id, returns projects that are copies of the project. + * + * @param projectId + * @return + */ + List getProjectCopies(Long projectId); + + /** + * Given a project, gets the project id for the project's root level project. + * + * @param project + * @return id of the root project + * @throws ObjectNotFoundException + */ + Long identifyRootProjectId(Project project) throws ObjectNotFoundException; + + long getNextAvailableProjectId(); + + Project copyProject(Long projectId, User user) throws Exception; + + void saveProjectContentToDisk(String projectJSONString, Project project) + throws FileNotFoundException, IOException; + + List getSharedTeacherPermissions(Project project, User sharedTeacher); + + SharedOwner addSharedTeacher(Long projectId, String username) + throws ObjectNotFoundException, TeacherAlreadySharedWithProjectException; + + void removeSharedTeacher(Long projectId, String username) throws ObjectNotFoundException; + + void addSharedTeacherPermission(Long projectId, Long userId, Integer permissionId) + throws ObjectNotFoundException; + + void removeSharedTeacherPermission(Long projectId, Long userId, Integer permissionId) + throws ObjectNotFoundException; + + void transferProjectOwnership(Project project, User newOwner) throws ObjectNotFoundException; + + List getProjectsWithoutRuns(User user); - List getAllSharedProjects(); - - Map getDirectoryInfo(File directory); + List getAllSharedProjects(); + + Map getDirectoryInfo(File directory); - String getProjectURI(Project project); + String getProjectURI(Project project); - String getProjectPath(Project project); + String getProjectPath(Project project); - String getLicensePath(Project project); + String getLicensePath(Project project); - String getProjectLocalPath(Project project); + String getProjectLocalPath(Project project); - List> getProjectSharedOwnersList(Project project); + List> getProjectSharedOwnersList(Project project); - void writeProjectLicenseFile(Project project) throws JSONException; + void writeProjectLicenseFile(Project project) throws JSONException; - void replaceMetadataInProjectJSONFile(String projectFilePath, ProjectMetadata metadata) - throws IOException, JSONException; + void replaceMetadataInProjectJSONFile(String projectFilePath, ProjectMetadata metadata) + throws IOException, JSONException; - void saveProjectToDatabase(Project project, User user, String projectJSONString) - throws JSONException, NotAuthorizedException; + void saveProjectToDatabase(Project project, User user, String projectJSONString) + throws JSONException, NotAuthorizedException; - void updateMetadataAndLicenseIfNecessary(Project project, String projectJSONString) - throws JSONException; + void updateMetadataAndLicenseIfNecessary(Project project, String projectJSONString) + throws JSONException; - void updateProjectNameIfNecessary(Project project, JSONObject projectMetadataJSON) - throws JSONException; + void updateProjectNameIfNecessary(Project project, JSONObject projectMetadataJSON) + throws JSONException; - String getProjectContent(Project project) throws IOException; + String getProjectContent(Project project) throws IOException; - ProjectComponent getProjectComponent(Project project, String nodeId, String componentId) - throws IOException, JSONException; + ProjectComponent getProjectComponent(Project project, String nodeId, String componentId) + throws IOException, JSONException; - void evictProjectContentCache(Long projectId); + void evictProjectContentCache(Long projectId); } diff --git a/src/main/java/org/wise/portal/service/project/impl/ProjectServiceImpl.java b/src/main/java/org/wise/portal/service/project/impl/ProjectServiceImpl.java index 01788c79a8..774ab33d20 100644 --- a/src/main/java/org/wise/portal/service/project/impl/ProjectServiceImpl.java +++ b/src/main/java/org/wise/portal/service/project/impl/ProjectServiceImpl.java @@ -67,12 +67,12 @@ import org.wise.portal.dao.ObjectNotFoundException; import org.wise.portal.dao.project.ProjectDao; import org.wise.portal.dao.run.RunDao; +import org.wise.portal.domain.Tag; import org.wise.portal.domain.authentication.MutableUserDetails; import org.wise.portal.domain.impl.AddSharedTeacherParameters; import org.wise.portal.domain.project.FamilyTag; import org.wise.portal.domain.project.Project; import org.wise.portal.domain.project.ProjectMetadata; -import org.wise.portal.domain.Tag; import org.wise.portal.domain.project.impl.PreviewProjectParameters; import org.wise.portal.domain.project.impl.ProjectComponent; import org.wise.portal.domain.project.impl.ProjectContent; diff --git a/src/main/java/org/wise/portal/service/usertags/UserTagsService.java b/src/main/java/org/wise/portal/service/usertags/UserTagsService.java new file mode 100644 index 0000000000..db15dffddd --- /dev/null +++ b/src/main/java/org/wise/portal/service/usertags/UserTagsService.java @@ -0,0 +1,22 @@ +package org.wise.portal.service.usertags; + +import java.util.Set; + +import org.wise.portal.domain.project.Project; +import org.wise.portal.domain.user.User; +import org.wise.portal.domain.usertag.UserTag; + +public interface UserTagsService { + + UserTag get(User user, String text); + + UserTag createTag(User user, String tag); + + Set getTags(User user, Project project); + + Boolean hasTag(User user, Project project, String tag); + + void applyTag(Project project, UserTag tag); + + void removeTag(Project project, UserTag tag); +} diff --git a/src/main/java/org/wise/portal/service/usertags/impl/UserTagsServiceImpl.java b/src/main/java/org/wise/portal/service/usertags/impl/UserTagsServiceImpl.java new file mode 100644 index 0000000000..9d70ad446a --- /dev/null +++ b/src/main/java/org/wise/portal/service/usertags/impl/UserTagsServiceImpl.java @@ -0,0 +1,74 @@ +package org.wise.portal.service.usertags.impl; + +import java.util.Set; +import java.util.stream.Collectors; + +import org.hibernate.proxy.HibernateProxyHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.acls.domain.ObjectIdentityImpl; +import org.springframework.security.acls.model.ObjectIdentity; +import org.springframework.stereotype.Service; +import org.wise.portal.dao.authentication.AclTargetObjectIdentityDao; +import org.wise.portal.dao.usertags.UserTagsDao; +import org.wise.portal.domain.authentication.MutableAclTargetObjectIdentity; +import org.wise.portal.domain.project.Project; +import org.wise.portal.domain.user.User; +import org.wise.portal.domain.usertag.UserTag; +import org.wise.portal.domain.usertag.impl.UserTagImpl; +import org.wise.portal.service.usertags.UserTagsService; + +@Service +public class UserTagsServiceImpl implements UserTagsService { + + @Autowired + private AclTargetObjectIdentityDao aclTargetObjectIdentityDao; + + @Autowired + private UserTagsDao userTagsDao; + + @Override + public UserTag get(User user, String text) { + return userTagsDao.get(user, text); + } + + @Override + public UserTag createTag(User user, String text) { + UserTag userTag = new UserTagImpl(user, text); + userTagsDao.save(userTag); + return userTag; + } + + @Override + public Set getTags(User user, Project project) { + MutableAclTargetObjectIdentity mutableObjectIdentity = getMutableObjectIdentity(project); + return mutableObjectIdentity.getTags().stream().filter(t -> t.getUser().equals(user)) + .collect(Collectors.toSet()); + } + + @Override + public Boolean hasTag(User user, Project project, String tag) { + MutableAclTargetObjectIdentity mutableObjectIdentity = getMutableObjectIdentity(project); + Set tags = mutableObjectIdentity.getTags(); + return tags.stream().anyMatch(t -> t.getUser().equals(user) && t.getText().equals(tag)); + } + + @Override + public void applyTag(Project project, UserTag tag) { + MutableAclTargetObjectIdentity mutableObjectIdentity = getMutableObjectIdentity(project); + mutableObjectIdentity.getTags().add(tag); + aclTargetObjectIdentityDao.save(mutableObjectIdentity); + } + + @Override + public void removeTag(Project project, UserTag tag) { + MutableAclTargetObjectIdentity mutableObjectIdentity = getMutableObjectIdentity(project); + mutableObjectIdentity.getTags().remove(tag); + aclTargetObjectIdentityDao.save(mutableObjectIdentity); + } + + private MutableAclTargetObjectIdentity getMutableObjectIdentity(Project project) { + ObjectIdentity objectIdentity = new ObjectIdentityImpl( + HibernateProxyHelper.getClassWithoutInitializingProxy(project), project.getId()); + return aclTargetObjectIdentityDao.retrieveByObjectIdentity(objectIdentity); + } +} diff --git a/src/main/resources/wise_db_init.sql b/src/main/resources/wise_db_init.sql index 5784358e21..b141cec755 100644 --- a/src/main/resources/wise_db_init.sql +++ b/src/main/resources/wise_db_init.sql @@ -44,7 +44,7 @@ create table acl_object_identity ( object_id_identity bigint not null, object_id_identity_num integer, entries_inheriting bit not null, - OPTLOCK integer, + OPTLOCK integer default 0, object_id_class bigint not null, owner_sid bigint, parent_object bigint, @@ -543,6 +543,22 @@ create table workgroups ( primary key (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `user_tags` ( + id bigint not null auto_increment, + users_fk bigint not null, + text varchar(100) not null, + constraint user_tags_users_fk foreign key (users_fk) references users (id), + primary key (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `acl_object_identity_to_user_tags` ( + acl_object_identity_fk bigint not null, + user_tags_fk bigint not null, + constraint acl_object_identity_to_user_tags_acl_object_identity_fk foreign key (acl_object_identity_fk) references acl_object_identity (id), + constraint acl_object_identity_to_user_tags_user_tags_fk foreign key (user_tags_fk) references user_tags (id), + primary key (acl_object_identity_fk, user_tags_fk) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + -- initial data for wise below INSERT INTO granted_authorities VALUES (1,'ROLE_USER',0),(2,'ROLE_ADMINISTRATOR',0),(3,'ROLE_TEACHER',0),(4,'ROLE_STUDENT',0),(5,'ROLE_AUTHOR',0),(6,'ROLE_RESEARCHER',0),(7,'ROLE_TRUSTED_AUTHOR',0),(8,'ROLE_TRANSLATOR',0);