Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2490: when copying a project make a copy of any attachments that are… #2539

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions service/src/main/java/skills/controller/UserSkillsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import skills.PublicProps;
import skills.auth.AuthMode;
import skills.auth.UserInfoService;
import skills.auth.aop.AdminOrApproverGetRequestUsersOnlyWhenUserIdSupplied;
import skills.auth.inviteOnly.InviteOnlyAccessDeniedException;
import skills.controller.exceptions.AttachmentValidator;
import skills.controller.exceptions.SkillException;
import skills.controller.exceptions.SkillsValidator;
Expand All @@ -42,19 +44,19 @@
import skills.controller.result.model.RequestResult;
import skills.controller.result.model.TableResult;
import skills.controller.result.model.UploadAttachmentResult;
import skills.controller.result.model.UserRoleRes;
import skills.dbupgrade.DBUpgradeSafe;
import skills.icons.CustomIconFacade;
import skills.services.AttachmentService;
import skills.services.SelfReportingService;
import skills.services.VersionService;
import skills.services.VideoCaptionsService;
import skills.services.*;
import skills.services.admin.InviteOnlyProjectService;
import skills.services.events.SkillEventResult;
import skills.services.events.SkillEventsService;
import skills.skillLoading.RankingLoader;
import skills.skillLoading.SkillsLoader;
import skills.skillLoading.SkillsService;
import skills.skillLoading.model.*;
import skills.storage.model.Attachment;
import skills.storage.model.auth.RoleName;
import skills.utils.MetricsLogger;

import jakarta.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -117,6 +119,12 @@ class UserSkillsController {
@Autowired
AttachmentService attachmentService;

@Autowired
InviteOnlyProjectService inviteOnlyProjectService;

@Autowired
AccessSettingsStorageService accessSettingsStorageService;

@Autowired
VideoCaptionsService videoCaptionsService;

Expand Down Expand Up @@ -528,6 +536,14 @@ public void download(@PathVariable("uuid") String uuid,
if (attachment == null) {
throw new SkillException("Attachment for uuid [" + uuid + "] does not exist");
}

if (attachment.getProjectId() != null && inviteOnlyProjectService.isInviteOnlyProject(attachment.getProjectId())) {
String userId = userInfoService.getCurrentUserId();
if (!inviteOnlyProjectService.isPrivateProjRoleOrAdminRole(attachment.getProjectId(), userId)) {
throw new InviteOnlyAccessDeniedException("Access is denied", attachment.getProjectId());
}
}

try (InputStream inputStream = attachment.getContent().getBinaryStream();
OutputStream outputStream = response.getOutputStream()) {
response.setContentType(attachment.getContentType());
Expand Down
49 changes: 45 additions & 4 deletions service/src/main/java/skills/services/AttachmentService.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ class AttachmentService {
)
}

@Transactional
Attachment copyAttachmentWithNewUuid(Attachment attachment, String newProjectId = null) {
String uuid = UUID.randomUUID().toString()
Attachment res = new Attachment(
filename: attachment.filename,
contentType: attachment.contentType,
uuid: uuid,
size: attachment.size,
userId: attachment.userId,
projectId: newProjectId ?: attachment.projectId,
quizId: newProjectId ? null : attachment.quizId, // then now a quiz for sure
skillId: newProjectId ? null : attachment.skillId, // if a new project then skillId may not exist
content: attachment.content
)
attachmentRepo.save(res)
return res
}

@Transactional(readOnly = true)
Attachment getAttachment(String uuid) {
return attachmentRepo.findByUuid(uuid)
Expand All @@ -87,11 +105,34 @@ class AttachmentService {
if (description) {
UUID_PATTERN.matcher(description).findAll().collect { it[1] }.each { uuid ->
Attachment attachment = attachmentRepo.findByUuid(uuid)
attachment.setProjectId(projectId)
attachment.setQuizId(quizId)
attachment.setSkillId(skillId)
attachmentRepo.save(attachment)
boolean changed = false
if (attachment.projectId != projectId) {
attachment.setProjectId(projectId)
changed = true
}
if (attachment.quizId != quizId) {
attachment.setQuizId(quizId)
changed = true
}

// only override if skill is not already set
// this can happen if user copy-and-pasted description
if (!attachment.skillId && attachment.skillId != skillId) {
attachment.setSkillId(skillId)
changed = true
}
if (changed) {
attachmentRepo.save(attachment)
}
}
}
}

List<String> findAttachmentUuids(String description) {
if (description) {
return UUID_PATTERN.matcher(description).findAll().collect { it[1] }
}
return []
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,15 @@ class InviteOnlyProjectService {
return roles?.find {it.roleName == RoleName.ROLE_PRIVATE_PROJECT_USER }
}

@Transactional(readOnly = true)
boolean isPrivateProjRoleOrAdminRole(String projectId, String userId) {
if (authMode == AuthMode.PKI) {
userId = userInfoService.getUserName(userId, true)
}
List<UserRoleRes> roles = accessSettingsStorageService.getUserRolesForProjectIdAndUserId(projectId, userId)
return roles?.find {it.roleName == RoleName.ROLE_PRIVATE_PROJECT_USER || it.roleName == RoleName.ROLE_PROJECT_ADMIN}
}

@Transactional(readOnly = true)
TableResult getPendingInvites(String projectId, String userEmailQuery, PageRequest pagingRequest) {
if (!isInviteOnlyProject(projectId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ class ProjectCopyService {
@PersistenceContext
EntityManager entityManager;

@Autowired
AttachmentService attachmentService

@Transactional
@Profile
void copyProject(String originalProjectId, ProjectRequest projectRequest) {
Expand Down Expand Up @@ -335,6 +338,7 @@ class ProjectCopyService {
BadgeRequest badgeRequest = new BadgeRequest()
Props.copy(fromBadge, badgeRequest)
badgeRequest.badgeId = fromBadge.skillId
badgeRequest.description = handleAttachmentsInDescription(badgeRequest.description, toProj.projectId)
badgeRequest.enabled = Boolean.FALSE.toString()
if(newIcons[fromBadge.iconClass]) {
badgeRequest.iconClass = newIcons[fromBadge.iconClass];
Expand All @@ -361,6 +365,7 @@ class ProjectCopyService {
SubjectRequest toSubj = new SubjectRequest()
Props.copy(fromSubj, toSubj)
toSubj.subjectId = fromSubj.skillId
toSubj.description = handleAttachmentsInDescription(toSubj.description, toProj.projectId)
if(newIcons[fromSubj.iconClass]) {
toSubj.iconClass = newIcons[fromSubj.iconClass]
}
Expand All @@ -380,41 +385,65 @@ class ProjectCopyService {
skillDefs?.findAll { it.enabled == "true" && (!it.copiedFrom) }
.sort { it.displayOrder }
.each { SkillDefWithExtra fromSkill ->

SkillProjectCopyRequest skillRequest = new SkillProjectCopyRequest()
Props.copy(fromSkill, skillRequest)
skillRequest.projectId = desProjectId
skillRequest.subjectId = subjectId
skillRequest.type = fromSkill.type?.toString()
skillRequest.version = 0

skillRequest.selfReportingType = fromSkill.selfReportingType?.toString()
if (skillRequest.selfReportingType && skillRequest.selfReportingType == SkillDef.SelfReportingType.Quiz.toString()) {
QuizToSkillDefRepo.QuizNameAndId quizNameAndId = quizToSkillDefRepo.getQuizIdBySkillIdRef(fromSkill.id)
skillRequest.quizId = quizNameAndId.quizId
}

if (fromSkill.type != SkillDef.ContainerType.SkillsGroup) {
skillRequest.numPerformToCompletion = fromSkill.totalPoints / fromSkill.pointIncrement
}

// group partial requirement must be set after skills are added
Integer groupNumSkillsRequired = -1
if (fromSkill.type == SkillDef.ContainerType.SkillsGroup) {
groupNumSkillsRequired = fromSkill.numSkillsRequired
skillRequest.numSkillsRequired = -1
}
SkillsAdminService.SaveSkillTmpRes saveSkillTmpRes = skillsAdminService.saveSkill(fromSkill.skillId, skillRequest, true, groupId, false)
if (fromSkill.type == SkillDef.ContainerType.SkillsGroup) {
createSkills(originalProjectId, desProjectId, subjectId, allCollectedSkills, fromSkill.skillId)
}
if (groupNumSkillsRequired > 0) {
skillRequest.numSkillsRequired = groupNumSkillsRequired
skillsAdminService.saveSkill(fromSkill.skillId, skillRequest, true, groupId)
try {
SkillProjectCopyRequest skillRequest = new SkillProjectCopyRequest()
Props.copy(fromSkill, skillRequest)
skillRequest.projectId = desProjectId
skillRequest.subjectId = subjectId
skillRequest.type = fromSkill.type?.toString()
skillRequest.version = 0
skillRequest.description = handleAttachmentsInDescription(skillRequest.description, desProjectId)

skillRequest.selfReportingType = fromSkill.selfReportingType?.toString()
if (skillRequest.selfReportingType && skillRequest.selfReportingType == SkillDef.SelfReportingType.Quiz.toString()) {
QuizToSkillDefRepo.QuizNameAndId quizNameAndId = quizToSkillDefRepo.getQuizIdBySkillIdRef(fromSkill.id)
skillRequest.quizId = quizNameAndId.quizId
}

if (fromSkill.type != SkillDef.ContainerType.SkillsGroup) {
skillRequest.numPerformToCompletion = fromSkill.totalPoints / fromSkill.pointIncrement
}

// group partial requirement must be set after skills are added
Integer groupNumSkillsRequired = -1
if (fromSkill.type == SkillDef.ContainerType.SkillsGroup) {
groupNumSkillsRequired = fromSkill.numSkillsRequired
skillRequest.numSkillsRequired = -1
}
SkillsAdminService.SaveSkillTmpRes saveSkillTmpRes = skillsAdminService.saveSkill(fromSkill.skillId, skillRequest, true, groupId, false)
if (fromSkill.type == SkillDef.ContainerType.SkillsGroup) {
createSkills(originalProjectId, desProjectId, subjectId, allCollectedSkills, fromSkill.skillId)
}
if (groupNumSkillsRequired > 0) {
skillRequest.numSkillsRequired = groupNumSkillsRequired
skillsAdminService.saveSkill(fromSkill.skillId, skillRequest, true, groupId)
}

handleVideoAttributes(fromSkill, saveSkillTmpRes)
} catch (Throwable t) {
throw new SkillException("Error copying skill: ${fromSkill.skillId}", t)
}
}
}

handleVideoAttributes(fromSkill, saveSkillTmpRes)
private final Map<String,String> copiedAttachmentUuids = [:]
@Profile
private String handleAttachmentsInDescription(String description, String newProjectId) {
String res = description
if (description) {
attachmentService.findAttachmentUuids(res).each { String uuid ->
Attachment attachment = attachmentService.getAttachment(uuid)
String copiedUuid = copiedAttachmentUuids[uuid]
if (!copiedUuid) {
Attachment copiedAttachment = attachmentService.copyAttachmentWithNewUuid(attachment, newProjectId)
copiedUuid = copiedAttachment.uuid
copiedAttachmentUuids[uuid] = copiedUuid
}
res = res.replaceAll(uuid, copiedUuid)
}
}

return res
}

@Profile
Expand Down
Loading
Loading