Skip to content

Commit

Permalink
feat: allow to share templates with other users and projects (backend)
Browse files Browse the repository at this point in the history
  • Loading branch information
codescape committed Feb 11, 2024
1 parent fd5370f commit 01e0d38
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 30 deletions.
2 changes: 1 addition & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ category: Administration

### [Unreleased]

* ...
* feat: allow to share templates with other users and projects (backend)

### [24.01.0] - 2024-01-27

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package de.codescape.jira.plugins.multiplesubtasks.action;

import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.project.Project;
import com.atlassian.jira.security.JiraAuthenticationContext;
Expand All @@ -9,10 +8,8 @@
import com.atlassian.jira.security.xsrf.RequiresXsrfCheck;
import com.atlassian.jira.web.action.JiraWebActionSupport;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import de.codescape.jira.plugins.multiplesubtasks.ao.SubtaskTemplate;
import de.codescape.jira.plugins.multiplesubtasks.model.CreatedSubtask;
import de.codescape.jira.plugins.multiplesubtasks.model.ShowSubtaskTemplate;
import de.codescape.jira.plugins.multiplesubtasks.model.SyntaxFormatException;
import de.codescape.jira.plugins.multiplesubtasks.service.MultipleSubtasksLicenseService;
import de.codescape.jira.plugins.multiplesubtasks.service.SubtaskTemplateService;
import de.codescape.jira.plugins.multiplesubtasks.service.SubtasksCreationService;
Expand Down Expand Up @@ -147,7 +144,7 @@ protected String doExecute() {
* Returns a list of all existing user templates for the currently logged-in user.
*/
public List<ShowSubtaskTemplate> getUserTemplates() {
return subtaskTemplateService.getUserTemplates(jiraAuthenticationContext.getLoggedInUser().getId())
return subtaskTemplateService.getUserTemplates(jiraAuthenticationContext.getLoggedInUser().getId(), true)
.stream()
.map(ShowSubtaskTemplate::new)
.collect(Collectors.toList());
Expand All @@ -161,7 +158,7 @@ public List<ShowSubtaskTemplate> getProjectTemplates() {
if (project == null) {
return Collections.emptyList();
}
return subtaskTemplateService.getProjectTemplates(project.getId())
return subtaskTemplateService.getProjectTemplates(project.getId(), true)
.stream()
.map(ShowSubtaskTemplate::new)
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public String getProjectKey() {
* Returns a list of all existing subtask templates for the current project.
*/
public List<ShowSubtaskTemplate> getProjectTemplates() {
return subtaskTemplateService.getProjectTemplates(projectManager.getProjectByCurrentKey(projectKey).getId())
return subtaskTemplateService.getProjectTemplates(projectManager.getProjectByCurrentKey(projectKey).getId(), false)
.stream()
.map(ShowSubtaskTemplate::new)
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ private long importQuickSubtasksTemplatesForProject(Project project, String temp
AtomicLong counter = new AtomicLong();
log.info("Extracting templates for project '" + project.getKey() + "'");
List<ShowSubtaskTemplate> templates = extractQuickSubtasksTemplatesFromXml(templatesForProject, true);
List<SubtaskTemplate> existingTemplates = subtaskTemplateService.getProjectTemplates(project.getId());
List<SubtaskTemplate> existingTemplates = subtaskTemplateService.getProjectTemplates(project.getId(), false);
templates.forEach(template -> {
log.info("Importing template for project '" + project.getKey() + "' with template name '" + template.getName() + "'");
if (existingTemplates.stream().noneMatch(existingTemplate ->
Expand All @@ -205,7 +205,7 @@ private long importQuickSubtasksTemplatesForUser(ApplicationUser applicationUser
AtomicLong counter = new AtomicLong();
log.info("Extracting templates for user '" + applicationUser.getUsername() + "'");
List<ShowSubtaskTemplate> templates = extractQuickSubtasksTemplatesFromXml(templatesForUser, false);
List<SubtaskTemplate> existingTemplates = subtaskTemplateService.getUserTemplates(applicationUser.getId());
List<SubtaskTemplate> existingTemplates = subtaskTemplateService.getUserTemplates(applicationUser.getId(), false);
templates.forEach(template -> {
log.info("Importing template for user '" + applicationUser.getUsername() + "' with template name '" + template.getName() + "'");
if (existingTemplates.stream().noneMatch(existingTemplate ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ protected String doExecute() {
* Returns a list of all existing user templates for the currently logged-in user.
*/
public List<ShowSubtaskTemplate> getUserTemplates() {
return subtaskTemplateService.getUserTemplates(currentUserId())
return subtaskTemplateService.getUserTemplates(currentUserId(), false)
.stream()
.map(ShowSubtaskTemplate::new)
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package de.codescape.jira.plugins.multiplesubtasks.ao;

import net.java.ao.schema.Indexed;
import net.java.ao.schema.NotNull;

public interface SubtaskShare extends BaseEntity {

/**
* The template that is shared.
*/
SubtaskTemplate getTemplate();

void setTemplate(SubtaskTemplate template);

/**
* (optional) The user the template is shared with.
*/
@Indexed
@NotNull
Long getUserId();

void setUserId(Long userId);

/**
* (optional) The project the template is shared with.
*/
@Indexed
Long getProjectId();

void setProjectId(Long projectId);

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.codescape.jira.plugins.multiplesubtasks.ao;

import de.codescape.jira.plugins.multiplesubtasks.model.SubtaskTemplateType;
import net.java.ao.OneToMany;
import net.java.ao.schema.Indexed;
import net.java.ao.schema.NotNull;
import net.java.ao.schema.StringLength;
Expand All @@ -10,31 +11,52 @@
*/
public interface SubtaskTemplate extends BaseEntity {

/**
* The user who owns and created this subtask template.
*/
@Indexed
@NotNull
Long getUserId();

void setUserId(Long userId);

/**
* The type of the subtask template.
*/
@NotNull
SubtaskTemplateType getTemplateType();

void setTemplateType(SubtaskTemplateType subtaskTemplateType);

/**
* (optional) The project this template is created for.
*/
@Indexed
Long getProjectId();

void setProjectId(Long projectId);

/**
* The name of the template.
*/
@NotNull
String getName();

void setName(String name);

/**
* The code of the template.
*/
@StringLength(StringLength.UNLIMITED)
@NotNull
String getTemplate();

void setTemplate(String template);

/**
* (optional) Shares for this template with other users or projects.
*/
@OneToMany(reverse = "getTemplate")
SubtaskShare[] getShares();

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.activeobjects.tx.Transactional;
import com.atlassian.jira.project.Project;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import de.codescape.jira.plugins.multiplesubtasks.ao.SubtaskConfig;
import de.codescape.jira.plugins.multiplesubtasks.ao.SubtaskShare;
import de.codescape.jira.plugins.multiplesubtasks.ao.SubtaskTemplate;
import de.codescape.jira.plugins.multiplesubtasks.model.SubtaskTemplateType;
import de.codescape.jira.plugins.multiplesubtasks.model.TemplateSortOrder;
Expand All @@ -13,7 +16,11 @@
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Subtask template service to persist and load templates from and to the database.
Expand All @@ -35,34 +42,45 @@ public SubtaskTemplateService(@ComponentImport ActiveObjects activeObjects,
/**
* Save a user specific subtask template.
*/
public void saveUserTemplate(Long userId, Long templateId, String templateName, String templateText) {
public SubtaskTemplate saveUserTemplate(Long userId, Long templateId, String templateName, String templateText) {
SubtaskTemplate subtaskTemplate;
if (templateId == null) {
SubtaskTemplate subtaskTemplate = activeObjects.create(SubtaskTemplate.class,
subtaskTemplate = activeObjects.create(SubtaskTemplate.class,
new DBParam("TEMPLATE_TYPE", SubtaskTemplateType.USER),
new DBParam("USER_ID", userId),
new DBParam("NAME", templateName),
new DBParam("TEMPLATE", templateText)
);
subtaskTemplate.save();
} else {
SubtaskTemplate subtaskTemplate = findUserTemplate(userId, templateId);
subtaskTemplate = findUserTemplate(userId, templateId);
if (subtaskTemplate != null) {
subtaskTemplate.setName(templateName);
subtaskTemplate.setTemplate(templateText);
subtaskTemplate.save();
}
}
return subtaskTemplate;
}

/**
* Return a list of all subtask templates for a user.
*/
public List<SubtaskTemplate> getUserTemplates(Long userId) {
return Arrays.asList(activeObjects.find(SubtaskTemplate.class,
public List<SubtaskTemplate> getUserTemplates(Long userId, boolean includeSharedTemplates) {
List<SubtaskTemplate> ownTemplates = Arrays.asList(activeObjects.find(SubtaskTemplate.class,
Query.select()
.where("USER_ID = ? and TEMPLATE_TYPE = ?", userId, SubtaskTemplateType.USER)
.order(getTemplateSortOrder()))
);
.order(getTemplateSortOrder())));
List<SubtaskTemplate> sharedTemplates = Collections.emptyList();
if (includeSharedTemplates) {
sharedTemplates = Arrays.asList(activeObjects.find(SubtaskTemplate.class,
Query.select()
.alias(SubtaskTemplate.class, "ST")
.alias(SubtaskShare.class, "SS")
.join(SubtaskShare.class, "ST.ID = SS.TEMPLATE_ID")
.where("SS.USER_ID = ? and ST.TEMPLATE_TYPE = ?", userId, SubtaskTemplateType.USER)));
}
return Stream.concat(ownTemplates.stream(), sharedTemplates.stream()).collect(Collectors.toList());
}

/**
Expand All @@ -71,6 +89,7 @@ public List<SubtaskTemplate> getUserTemplates(Long userId) {
public void deleteUserTemplate(Long userId, Long templateId) {
SubtaskTemplate subtaskTemplate = findUserTemplate(userId, templateId);
if (subtaskTemplate != null) {
Arrays.stream(subtaskTemplate.getShares()).forEach(activeObjects::delete);
activeObjects.delete(subtaskTemplate);
}
}
Expand All @@ -88,9 +107,10 @@ public SubtaskTemplate findUserTemplate(Long userId, Long templateId) {
/**
* Save a project specific subtask template.
*/
public void saveProjectTemplate(Long projectId, Long userId, Long templateId, String templateName, String templateText) {
public SubtaskTemplate saveProjectTemplate(Long projectId, Long userId, Long templateId, String templateName, String templateText) {
SubtaskTemplate subtaskTemplate;
if (templateId == null) {
SubtaskTemplate subtaskTemplate = activeObjects.create(SubtaskTemplate.class,
subtaskTemplate = activeObjects.create(SubtaskTemplate.class,
new DBParam("TEMPLATE_TYPE", SubtaskTemplateType.PROJECT),
new DBParam("PROJECT_ID", projectId),
new DBParam("USER_ID", userId),
Expand All @@ -99,25 +119,35 @@ public void saveProjectTemplate(Long projectId, Long userId, Long templateId, St
);
subtaskTemplate.save();
} else {
SubtaskTemplate subtaskTemplate = findProjectTemplate(projectId, templateId);
subtaskTemplate = findProjectTemplate(projectId, templateId);
if (subtaskTemplate != null) {
subtaskTemplate.setUserId(userId);
subtaskTemplate.setName(templateName);
subtaskTemplate.setTemplate(templateText);
subtaskTemplate.save();
}
}
return subtaskTemplate;
}

/**
* Return a list of all subtask templates for a project.
*/
public List<SubtaskTemplate> getProjectTemplates(Long projectId) {
return Arrays.asList(activeObjects.find(SubtaskTemplate.class,
public List<SubtaskTemplate> getProjectTemplates(Long projectId, boolean includeSharedTemplates) {
List<SubtaskTemplate> ownTemplates = Arrays.asList(activeObjects.find(SubtaskTemplate.class,
Query.select()
.where("PROJECT_ID = ? and TEMPLATE_TYPE = ?", projectId, SubtaskTemplateType.PROJECT)
.order(getTemplateSortOrder()))
);
.order(getTemplateSortOrder())));
List<SubtaskTemplate> sharedTemplates = Collections.emptyList();
if (includeSharedTemplates) {
sharedTemplates = Arrays.asList(activeObjects.find(SubtaskTemplate.class,
Query.select()
.alias(SubtaskTemplate.class, "ST")
.alias(SubtaskShare.class, "SS")
.join(SubtaskShare.class, "ST.ID = SS.TEMPLATE_ID")
.where("SS.PROJECT_ID = ? and ST.TEMPLATE_TYPE = ?", projectId, SubtaskTemplateType.PROJECT)));
}
return Stream.concat(ownTemplates.stream(), sharedTemplates.stream()).collect(Collectors.toList());
}

/**
Expand All @@ -126,6 +156,7 @@ public List<SubtaskTemplate> getProjectTemplates(Long projectId) {
public void deleteProjectTemplate(Long projectId, Long templateId) {
SubtaskTemplate subtaskTemplate = findProjectTemplate(projectId, templateId);
if (subtaskTemplate != null) {
Arrays.stream(subtaskTemplate.getShares()).forEach(activeObjects::delete);
activeObjects.delete(subtaskTemplate);
}
}
Expand All @@ -152,24 +183,26 @@ private String getTemplateSortOrder() {
/**
* Save a global subtask template.
*/
public void saveGlobalTemplate(Long userId, Long templateId, String templateName, String templateText) {
public SubtaskTemplate saveGlobalTemplate(Long userId, Long templateId, String templateName, String templateText) {
SubtaskTemplate subtaskTemplate;
if (templateId == null) {
SubtaskTemplate subtaskTemplate = activeObjects.create(SubtaskTemplate.class,
subtaskTemplate = activeObjects.create(SubtaskTemplate.class,
new DBParam("TEMPLATE_TYPE", SubtaskTemplateType.GLOBAL),
new DBParam("USER_ID", userId),
new DBParam("NAME", templateName),
new DBParam("TEMPLATE", templateText)
);
subtaskTemplate.save();
} else {
SubtaskTemplate subtaskTemplate = findGlobalTemplate(templateId);
subtaskTemplate = findGlobalTemplate(templateId);
if (subtaskTemplate != null) {
subtaskTemplate.setUserId(userId);
subtaskTemplate.setName(templateName);
subtaskTemplate.setTemplate(templateText);
subtaskTemplate.save();
}
}
return subtaskTemplate;
}

/**
Expand All @@ -188,6 +221,7 @@ public SubtaskTemplate findGlobalTemplate(Long templateId) {
public void deleteGlobalTemplate(Long templateId) {
SubtaskTemplate subtaskTemplate = findGlobalTemplate(templateId);
if (subtaskTemplate != null) {
Arrays.stream(subtaskTemplate.getShares()).forEach(activeObjects::delete);
activeObjects.delete(subtaskTemplate);
}
}
Expand All @@ -203,4 +237,38 @@ public List<SubtaskTemplate> getGlobalTemplates() {
);
}

/**
* Create a share for a given user template with a given user.
* <p>
* A template can never be shared with the template owner and not with the same user more than one time.
*/
public void shareUserTemplate(SubtaskTemplate template, ApplicationUser user) {
SubtaskShare[] subtaskShares = activeObjects.find(SubtaskShare.class,
Query.select()
.where("TEMPLATE_ID = ? and USER_ID = ?", template.getID(), user.getId()));
if (!Objects.equals(template.getUserId(), user.getId()) && subtaskShares.length == 0) {
SubtaskShare subtaskShare = activeObjects.create(SubtaskShare.class,
new DBParam("TEMPLATE_ID", template.getID()),
new DBParam("USER_ID", user.getId()));
subtaskShare.save();
}
}

/**
* Create a share for a given project template with a given project.
* <p>
* A template can never be shared with the owning project and not with the same project more than one time.
*/
public void shareProjectTemplate(SubtaskTemplate template, Project project) {
SubtaskShare[] subtaskShares = activeObjects.find(SubtaskShare.class,
Query.select()
.where("TEMPLATE_ID = ? and PROJECT_ID = ?", template.getID(), project.getId()));
if (!Objects.equals(template.getProjectId(), project.getId()) && subtaskShares.length == 0) {
SubtaskShare subtaskShare = activeObjects.create(SubtaskShare.class,
new DBParam("TEMPLATE_ID", template.getID()),
new DBParam("PROJECT_ID", project.getId()));
subtaskShare.save();
}
}

}
Loading

0 comments on commit 01e0d38

Please sign in to comment.