/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.modules.portfolio.manager;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.olat.basesecurity.Group;
import org.olat.basesecurity.IdentityRef;
import org.olat.basesecurity.manager.GroupDAO;
import org.olat.core.commons.modules.bc.vfs.OlatRootFileImpl;
import org.olat.core.commons.persistence.DB;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.FileUtils;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.resource.OresHelper;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.core.util.xml.XStreamHelper;
import org.olat.course.CourseFactory;
import org.olat.course.ICourse;
import org.olat.course.assessment.AssessmentHelper;
import org.olat.course.nodes.CourseNode;
import org.olat.course.nodes.PortfolioCourseNode;
import org.olat.course.run.scoring.AssessmentEvaluation;
import org.olat.course.run.scoring.ScoreEvaluation;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.fileresource.FileResourceManager;
import org.olat.modules.assessment.AssessmentEntry;
import org.olat.modules.assessment.AssessmentService;
import org.olat.modules.assessment.model.AssessmentEntryStatus;
import org.olat.modules.forms.EvaluationFormSessionStatus;
import org.olat.modules.forms.manager.EvaluationFormSessionDAO;
import org.olat.modules.portfolio.AssessmentSection;
import org.olat.modules.portfolio.Assignment;
import org.olat.modules.portfolio.AssignmentStatus;
import org.olat.modules.portfolio.AssignmentType;
import org.olat.modules.portfolio.Binder;
import org.olat.modules.portfolio.BinderDeliveryOptions;
import org.olat.modules.portfolio.BinderLight;
import org.olat.modules.portfolio.BinderRef;
import org.olat.modules.portfolio.Category;
import org.olat.modules.portfolio.CategoryToElement;
import org.olat.modules.portfolio.Media;
import org.olat.modules.portfolio.MediaHandler;
import org.olat.modules.portfolio.MediaLight;
import org.olat.modules.portfolio.Page;
import org.olat.modules.portfolio.PageBody;
import org.olat.modules.portfolio.PageImageAlign;
import org.olat.modules.portfolio.PagePart;
import org.olat.modules.portfolio.PageStatus;
import org.olat.modules.portfolio.PortfolioElement;
import org.olat.modules.portfolio.PortfolioElementType;
import org.olat.modules.portfolio.PortfolioRoles;
import org.olat.modules.portfolio.PortfolioService;
import org.olat.modules.portfolio.Section;
import org.olat.modules.portfolio.SectionRef;
import org.olat.modules.portfolio.SectionStatus;
import org.olat.modules.portfolio.handler.BinderTemplateResource;
import org.olat.modules.portfolio.model.AccessRightChange;
import org.olat.modules.portfolio.model.AccessRights;
import org.olat.modules.portfolio.model.AssessedBinder;
import org.olat.modules.portfolio.model.AssessmentSectionChange;
import org.olat.modules.portfolio.model.AssessmentSectionImpl;
import org.olat.modules.portfolio.model.AssignmentImpl;
import org.olat.modules.portfolio.model.BinderImpl;
import org.olat.modules.portfolio.model.BinderPageUsage;
import org.olat.modules.portfolio.model.BinderStatistics;
import org.olat.modules.portfolio.model.CategoryLight;
import org.olat.modules.portfolio.model.PageImpl;
import org.olat.modules.portfolio.model.SectionImpl;
import org.olat.modules.portfolio.model.SectionKeyRef;
import org.olat.modules.portfolio.model.SynchedBinder;
import org.olat.modules.portfolio.ui.PortfolioHomeController;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryEntryRef;
import org.olat.repository.RepositoryService;
import org.olat.resource.OLATResource;
import org.olat.resource.OLATResourceManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.thoughtworks.xstream.XStream;
/**
*
* Initial date: 06.06.2016<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
@Service
public class PortfolioServiceImpl implements PortfolioService {
private static final OLog log = Tracing.createLoggerFor(PortfolioServiceImpl.class);
private static XStream configXstream = XStreamHelper.createXStreamInstance();
static {
configXstream.alias("deliveryOptions", BinderDeliveryOptions.class);
}
@Autowired
private DB dbInstance;
@Autowired
private PageDAO pageDao;
@Autowired
private GroupDAO groupDao;
@Autowired
private MediaDAO mediaDao;
@Autowired
private BinderDAO binderDao;
@Autowired
private CommentDAO commentDao;
@Autowired
private CategoryDAO categoryDao;
@Autowired
private AssignmentDAO assignmentDao;
@Autowired
private SharedByMeQueries sharedByMeQueries;
@Autowired
private OLATResourceManager resourceManager;
@Autowired
private SharedWithMeQueries sharedWithMeQueries;
@Autowired
private PortfolioFileStorage portfolioFileStorage;
@Autowired
private AssessmentSectionDAO assessmentSectionDao;
@Autowired
private AssessmentService assessmentService;
@Autowired
private RepositoryService repositoryService;
@Autowired
private EvaluationFormSessionDAO evaluationFormSessionDao;
@Autowired
private BinderUserInformationsDAO binderUserInformationsDao;
@Autowired
private List<MediaHandler> mediaHandlers;
@Override
public Binder createNewBinder(String title, String summary, String imagePath, Identity owner) {
BinderImpl portfolio = binderDao.createAndPersist(title, summary, imagePath, null);
if(owner != null) {
groupDao.addMembershipTwoWay(portfolio.getBaseGroup(), owner, PortfolioRoles.owner.name());
}
return portfolio;
}
@Override
public OLATResource createBinderTemplateResource() {
OLATResource resource = resourceManager.createOLATResourceInstance(BinderTemplateResource.TYPE_NAME);
return resource;
}
@Override
public void createAndPersistBinderTemplate(Identity owner, RepositoryEntry entry, Locale locale) {
BinderImpl binder = binderDao.createAndPersist(entry.getDisplayname(), entry.getDescription(), null, entry);
if(owner != null) {
groupDao.addMembershipTwoWay(binder.getBaseGroup(), owner, PortfolioRoles.owner.name());
}
//add section
Translator pt = Util.createPackageTranslator(PortfolioHomeController.class, locale);
String sectionTitle = pt.translate("new.section.title");
String sectionDescription = pt.translate("new.section.desc");
binderDao.createSection(sectionTitle, sectionDescription, null, null, binder);
}
@Override
public Binder updateBinder(Binder binder) {
return binderDao.updateBinder(binder);
}
@Override
public Binder copyBinder(Binder transientBinder, RepositoryEntry entry) {
String imagePath = null;
if(StringHelper.containsNonWhitespace(transientBinder.getImagePath())) {
File bcroot = portfolioFileStorage.getRootDirectory();
File image = new File(bcroot, transientBinder.getImagePath());
if(image.exists()) {
imagePath = addPosterImageForBinder(image, image.getName());
}
}
return internalCopyTransientBinder(transientBinder, entry, imagePath, true);
}
@Override
public Binder importBinder(Binder transientBinder, RepositoryEntry templateEntry, File image) {
String imagePath = null;
if(StringHelper.containsNonWhitespace(transientBinder.getImagePath())) {
imagePath = addPosterImageForBinder(image, image.getName());
}
return internalCopyTransientBinder(transientBinder, templateEntry, imagePath, false);
}
private Binder internalCopyTransientBinder(Binder transientBinder, RepositoryEntry entry, String imagePath, boolean copy) {
Binder binder = binderDao.createAndPersist(transientBinder.getTitle(), transientBinder.getSummary(), imagePath, entry);
//copy sections
for(Section transientSection:((BinderImpl)transientBinder).getSections()) {
SectionImpl section = binderDao.createSection(transientSection.getTitle(), transientSection.getDescription(),
transientSection.getBeginDate(), transientSection.getEndDate(), binder);
List<Assignment> transientAssignments = ((SectionImpl)transientSection).getAssignments();
for(Assignment transientAssignment:transientAssignments) {
if(transientAssignment != null) {
File newStorage = portfolioFileStorage.generateAssignmentSubDirectory();
String storage = portfolioFileStorage.getRelativePath(newStorage);
assignmentDao.createAssignment(transientAssignment.getTitle(), transientAssignment.getSummary(),
transientAssignment.getContent(), storage, transientAssignment.getAssignmentType(),
transientAssignment.getAssignmentStatus(), section,
transientAssignment.isOnlyAutoEvaluation(), transientAssignment.isReviewerSeeAutoEvaluation(),
transientAssignment.isAnonymousExternalEvaluation(), transientAssignment.getFormEntry());
//copy attachments
File templateDirectory = portfolioFileStorage.getAssignmentDirectory(transientAssignment);
if(copy && templateDirectory != null) {
FileUtils.copyDirContentsToDir(templateDirectory, newStorage, false, "Assignment attachments");
}
}
}
}
return binder;
}
@Override
public boolean deleteBinderTemplate(Binder binder, RepositoryEntry templateEntry) {
BinderImpl reloadedBinder = (BinderImpl)binderDao.loadByKey(binder.getKey());
int deletedRows = binderDao.deleteBinderTemplate(reloadedBinder);
return deletedRows > 0;
}
@Override
public boolean deleteBinder(BinderRef binder) {
int rows = binderDao.deleteBinder(binder);
return rows > 0;
}
@Override
public BinderDeliveryOptions getDeliveryOptions(OLATResource resource) {
FileResourceManager frm = FileResourceManager.getInstance();
File reFolder = frm.getFileResourceRoot(resource);
File configXml = new File(reFolder, PACKAGE_CONFIG_FILE_NAME);
BinderDeliveryOptions config;
if(configXml.exists()) {
config = (BinderDeliveryOptions)configXstream.fromXML(configXml);
} else {
//set default config
config = BinderDeliveryOptions.defaultOptions();
setDeliveryOptions(resource, config);
}
return config;
}
@Override
public void setDeliveryOptions(OLATResource resource, BinderDeliveryOptions options) {
FileResourceManager frm = FileResourceManager.getInstance();
File reFolder = frm.getFileResourceRoot(resource);
File configXml = new File(reFolder, PACKAGE_CONFIG_FILE_NAME);
if(options == null) {
if(configXml.exists()) {
configXml.delete();
}
} else {
try (OutputStream out = new FileOutputStream(configXml)) {
configXstream.toXML(options, out);
} catch (IOException e) {
log.error("", e);
}
}
}
@Override
public Assignment addAssignment(String title, String summary, String content, AssignmentType type, Section section,
boolean onlyAutoEvaluation, boolean reviewerSeeAutoEvaluation, boolean anonymousExternEvaluation, RepositoryEntry formEntry) {
File newStorage = portfolioFileStorage.generateAssignmentSubDirectory();
String storage = portfolioFileStorage.getRelativePath(newStorage);
Section reloadedSection = binderDao.loadSectionByKey(section.getKey());
return assignmentDao.createAssignment(title, summary, content, storage, type,
AssignmentStatus.template, reloadedSection,
onlyAutoEvaluation, reviewerSeeAutoEvaluation, anonymousExternEvaluation, formEntry);
}
@Override
public Assignment updateAssignment(Assignment assignment, String title, String summary, String content, AssignmentType type,
boolean onlyAutoEvaluation, boolean reviewerSeeAutoEvaluation, boolean anonymousExternEvaluation, RepositoryEntry formEntry) {
if(!StringHelper.containsNonWhitespace(assignment.getStorage())) {
File newStorage = portfolioFileStorage.generateAssignmentSubDirectory();
String newRelativeStorage = portfolioFileStorage.getRelativePath(newStorage);
((AssignmentImpl)assignment).setStorage(newRelativeStorage);
}
AssignmentImpl impl = (AssignmentImpl)assignment;
impl.setTitle(title);
impl.setSummary(summary);
impl.setContent(content);
impl.setType(type.name());
impl.setOnlyAutoEvaluation(onlyAutoEvaluation);
impl.setReviewerSeeAutoEvaluation(reviewerSeeAutoEvaluation);
impl.setAnonymousExternalEvaluation(anonymousExternEvaluation);
impl.setFormEntry(formEntry);
return assignmentDao.updateAssignment(assignment);
}
@Override
public Section moveUpAssignment(Section section, Assignment assignment) {
Section reloadedSection = binderDao.loadSectionByKey(section.getKey());
return assignmentDao.moveUpAssignment((SectionImpl)reloadedSection, assignment);
}
@Override
public Section moveDownAssignment(Section section, Assignment assignment) {
Section reloadedSection = binderDao.loadSectionByKey(section.getKey());
return assignmentDao.moveDownAssignment((SectionImpl)reloadedSection, assignment);
}
@Override
public void moveAssignment(SectionRef currentSectionRef, Assignment assignment, SectionRef newParentSectionRef) {
Section currentSection = binderDao.loadSectionByKey(currentSectionRef.getKey());
Section newParentSection = binderDao.loadSectionByKey(newParentSectionRef.getKey());
assignmentDao.moveAssignment((SectionImpl)currentSection, assignment, (SectionImpl)newParentSection);
}
@Override
public List<Assignment> getAssignments(PortfolioElement element, String searchString) {
if(element.getType() == PortfolioElementType.binder) {
return assignmentDao.loadAssignments((BinderRef)element, searchString);
}
if(element.getType() == PortfolioElementType.section) {
return assignmentDao.loadAssignments((SectionRef)element, searchString);
}
if(element.getType() == PortfolioElementType.page) {
return assignmentDao.loadAssignments((Page)element, searchString);
}
return null;
}
@Override
public List<Assignment> searchOwnedAssignments(IdentityRef assignee) {
return assignmentDao.getOwnedAssignments(assignee);
}
@Override
public boolean isAssignmentInUse(Assignment assignment) {
return assignmentDao.isAssignmentInUse(assignment);
}
@Override
public boolean deleteAssignment(Assignment assignment) {
Assignment reloadedAssignment = assignmentDao.loadAssignmentByKey(assignment.getKey());
Section reloadedSection = reloadedAssignment.getSection();
boolean removed = false;
if(reloadedSection != null) {
removed = ((SectionImpl)reloadedSection).getAssignments().remove(reloadedAssignment);
}
assignmentDao.deleteAssignment(reloadedAssignment);
if(removed) {
binderDao.updateSection(reloadedSection);
}
return true;
}
@Override
public Assignment startAssignment(Assignment assignment, Identity author) {
Assignment reloadedAssignment = assignmentDao.loadAssignmentByKey(assignment.getKey());
if(reloadedAssignment.getAssignmentType() == AssignmentType.essay) {
if(reloadedAssignment.getPage() == null) {
Section section = reloadedAssignment.getSection();
Page page = appendNewPage(author, reloadedAssignment.getTitle(), reloadedAssignment.getSummary(), null, null, section);
reloadedAssignment = assignmentDao.startEssayAssignment(reloadedAssignment, page, author);
}
} else if(reloadedAssignment.getAssignmentType() == AssignmentType.document) {
if(reloadedAssignment.getPage() == null) {
Section section = reloadedAssignment.getSection();
Page page = appendNewPage(author, reloadedAssignment.getTitle(), reloadedAssignment.getSummary(), null, null, section);
reloadedAssignment = assignmentDao.startEssayAssignment(reloadedAssignment, page, author);
}
} else if(reloadedAssignment.getAssignmentType() == AssignmentType.form) {
if(reloadedAssignment.getPage() == null) {
Section section = reloadedAssignment.getSection();
RepositoryEntry formEntry = reloadedAssignment.getFormEntry();
Page page = appendNewPage(author, reloadedAssignment.getTitle(), reloadedAssignment.getSummary(), null, false, null, section);
reloadedAssignment = assignmentDao.startFormAssignment(reloadedAssignment, page, author);
//create the session for the assignee
evaluationFormSessionDao.createSessionForPortfolio(author, page.getBody(), formEntry);
}
}
dbInstance.commit();
return reloadedAssignment;
}
@Override
public Assignment getAssignment(PageBody body) {
return assignmentDao.loadAssignment(body);
}
@Override
public SectionRef appendNewSection(String title, String description, Date begin, Date end, BinderRef binder) {
Binder reloadedBinder = binderDao.loadByKey(binder.getKey());
SectionImpl newSection = binderDao.createSection(title, description, begin, end, reloadedBinder);
return new SectionKeyRef(newSection.getKey());
}
@Override
public Section updateSection(Section section) {
return binderDao.updateSection(section);
}
@Override
public List<Section> getSections(BinderRef binder) {
return binderDao.getSections(binder);
}
@Override
public Section getSection(SectionRef section) {
return binderDao.loadSectionByKey(section.getKey());
}
@Override
public Binder moveUpSection(Binder binder, Section section) {
Binder reloadedBinder = binderDao.loadByKey(binder.getKey());
return binderDao.moveUpSection((BinderImpl)reloadedBinder, section);
}
@Override
public Binder moveDownSection(Binder binder, Section section) {
Binder reloadedBinder = binderDao.loadByKey(binder.getKey());
return binderDao.moveDownSection((BinderImpl)reloadedBinder, section);
}
@Override
public Binder deleteSection(Binder binder, Section section) {
Section reloadedSection = binderDao.loadSectionByKey(section.getKey());
Binder reloadedBinder = reloadedSection.getBinder();
return binderDao.deleteSection(reloadedBinder, reloadedSection);
}
@Override
public List<Page> getPages(BinderRef binder, String searchString) {
return pageDao.getPages(binder, searchString);
}
@Override
public List<Page> getPages(SectionRef section) {
return pageDao.getPages(section);
}
@Override
public List<Binder> getOwnedBinders(IdentityRef owner) {
return binderDao.getOwnedBinders(owner);
}
@Override
public List<BinderStatistics> searchOwnedBinders(IdentityRef owner) {
return binderDao.searchOwnedBinders(owner, false);
}
@Override
public List<BinderStatistics> searchOwnedDeletedBinders(IdentityRef owner) {
return binderDao.searchOwnedBinders(owner, true);
}
@Override
public List<Binder> searchOwnedBindersFromCourseTemplate(IdentityRef owner) {
return binderDao.getOwnedBinderFromCourseTemplate(owner);
}
@Override
public List<Binder> searchSharedBindersBy(Identity owner, String searchString) {
return sharedByMeQueries.searchSharedBinders(owner, searchString);
}
@Override
public List<AssessedBinder> searchSharedBindersWith(Identity coach, String searchString) {
return sharedWithMeQueries.searchSharedBinders(coach, searchString);
}
@Override
public List<RepositoryEntry> searchCourseWithBinderTemplates(Identity participant) {
return binderDao.searchCourseTemplates(participant);
}
@Override
public Binder getBinderByKey(Long portfolioKey) {
return binderDao.loadByKey(portfolioKey);
}
@Override
public void updateBinderUserInformations(Binder binder, Identity user) {
if(binder == null || user == null) return;
binderUserInformationsDao.updateBinderUserInformations(binder, user);
}
@Override
public Binder getBinderByResource(OLATResource resource) {
return binderDao.loadByResource(resource);
}
@Override
public BinderStatistics getBinderStatistics(BinderRef binder) {
return binderDao.getBinderStatistics(binder);
}
@Override
public RepositoryEntry getRepositoryEntry(Binder binder) {
OLATResource resource = ((BinderImpl)binder).getOlatResource();
Long resourceKey = resource.getKey();
return repositoryService.loadByResourceKey(resourceKey);
}
@Override
public Binder getBinderBySection(SectionRef section) {
return binderDao.loadBySection(section);
}
@Override
public boolean isTemplateInUse(Binder binder, RepositoryEntry courseEntry, String subIdent) {
return binderDao.isTemplateInUse(binder, courseEntry, subIdent);
}
@Override
public Binder getBinder(Identity owner, BinderRef templateBinder, RepositoryEntryRef courseEntry, String subIdent) {
return binderDao.getBinder(owner, templateBinder, courseEntry, subIdent);
}
@Override
public List<Binder> getBinders(Identity owner, RepositoryEntryRef courseEntry, String subIdent) {
return binderDao.getBinders(owner, courseEntry, subIdent);
}
@Override
public Binder assignBinder(Identity owner, BinderRef templateBinder, RepositoryEntry entry, String subIdent, Date deadline) {
BinderImpl reloadedTemplate = (BinderImpl)binderDao.loadByKey(templateBinder.getKey());
BinderImpl binder = binderDao.createCopy(reloadedTemplate, entry, subIdent);
groupDao.addMembershipTwoWay(binder.getBaseGroup(), owner, PortfolioRoles.owner.name());
return binder;
}
@Override
public SynchedBinder loadAndSyncBinder(BinderRef binder) {
Binder reloadedBinder = binderDao.loadByKey(binder.getKey());
AtomicBoolean changes = new AtomicBoolean(false);
if(reloadedBinder.getTemplate() != null) {
reloadedBinder = binderDao
.syncWithTemplate((BinderImpl)reloadedBinder.getTemplate(), (BinderImpl)reloadedBinder, changes);
}
return new SynchedBinder(reloadedBinder, changes.get());
}
@Override
public boolean isMember(BinderRef binder, IdentityRef identity, String... roles) {
return binderDao.isMember(binder, identity, roles);
}
@Override
public List<Identity> getMembers(BinderRef binder, String... roles) {
return binderDao.getMembers(binder, roles);
}
@Override
public List<Identity> getMembers(Page page, String... roles) {
return pageDao.getMembers(page, roles);
}
@Override
public boolean isBinderVisible(IdentityRef identity, BinderRef binder) {
return binderDao.isMember(binder, identity,
PortfolioRoles.owner.name(), PortfolioRoles.coach.name(),
PortfolioRoles.reviewer.name(), PortfolioRoles.invitee.name());
}
@Override
public List<AccessRights> getAccessRights(Binder binder) {
List<AccessRights> rights = binderDao.getBinderAccesRights(binder, null);
List<AccessRights> sectionRights = binderDao.getSectionAccesRights(binder, null);
rights.addAll(sectionRights);
List<AccessRights> pageRights = binderDao.getPageAccesRights(binder, null);
rights.addAll(pageRights);
return rights;
}
@Override
public List<AccessRights> getAccessRights(Binder binder, Identity identity) {
List<AccessRights> rights = binderDao.getBinderAccesRights(binder, identity);
List<AccessRights> sectionRights = binderDao.getSectionAccesRights(binder, identity);
rights.addAll(sectionRights);
List<AccessRights> pageRights = binderDao.getPageAccesRights(binder, identity);
rights.addAll(pageRights);
return rights;
}
@Override
public List<AccessRights> getAccessRights(Page page) {
List<AccessRights> rights = binderDao.getBinderAccesRights(page);
List<AccessRights> sectionRights = binderDao.getSectionAccesRights(page);
rights.addAll(sectionRights);
List<AccessRights> pageRights = binderDao.getPageAccesRights(page);
rights.addAll(pageRights);
return rights;
}
@Override
public void addAccessRights(PortfolioElement element, Identity identity, PortfolioRoles role) {
Group baseGroup = element.getBaseGroup();
if(!groupDao.hasRole(baseGroup, identity, role.name())) {
groupDao.addMembershipTwoWay(baseGroup, identity, role.name());
}
}
@Override
public void changeAccessRights(List<Identity> identities, List<AccessRightChange> changes) {
for(Identity identity:identities) {
for(AccessRightChange change:changes) {
Group baseGroup = change.getElement().getBaseGroup();
if(change.isAdd()) {
if(!groupDao.hasRole(baseGroup, identity, change.getRole().name())) {
Group group = getGroup(change.getElement());
groupDao.addMembershipOneWay(group, identity, change.getRole().name());
}
} else {
if(groupDao.hasRole(baseGroup, identity, change.getRole().name())) {
Group group = getGroup(change.getElement());
groupDao.removeMembership(group, identity, change.getRole().name());
}
}
}
}
}
@Override
public void removeAccessRights(Binder binder, Identity identity) {
List<AccessRights> rights = getAccessRights(binder, identity);
for(AccessRights right:rights) {
Group baseGroup;
if(right.getType() == PortfolioElementType.binder) {
baseGroup = binderDao.loadByKey(right.getBinderKey()).getBaseGroup();
} else if(right.getType() == PortfolioElementType.section) {
baseGroup = binderDao.loadSectionByKey(right.getSectionKey()).getBaseGroup();
} else if(right.getType() == PortfolioElementType.page) {
baseGroup = pageDao.loadByKey(right.getPageKey()).getBaseGroup();
} else {
continue;
}
if(groupDao.hasRole(baseGroup, identity, right.getRole().name())) {
groupDao.removeMembership(baseGroup, identity, right.getRole().name());
}
}
}
private Group getGroup(PortfolioElement element) {
if(element instanceof Page) {
return pageDao.getGroup((Page)element);
}
if(element instanceof SectionRef) {
return binderDao.getGroup((SectionRef)element);
}
if(element instanceof BinderRef) {
return binderDao.getGroup((BinderRef)element);
}
return null;
}
@Override
public List<Category> getCategories(PortfolioElement element) {
OLATResourceable ores = getOLATResoucreable(element);
return categoryDao.getCategories(ores);
}
@Override
public List<CategoryToElement> getCategorizedSectionsAndPages(BinderRef binder) {
return categoryDao.getCategorizedSectionsAndPages(binder);
}
@Override
public List<CategoryToElement> getCategorizedSectionAndPages(SectionRef section) {
return categoryDao.getCategorizedSectionAndPages(section);
}
@Override
public List<CategoryToElement> getCategorizedOwnedPages(IdentityRef owner) {
return categoryDao.getCategorizedOwnedPages(owner);
}
@Override
public void updateCategories(PortfolioElement element, List<String> categories) {
OLATResourceable ores = getOLATResoucreable(element);
updateCategories(ores, categories);
}
private OLATResourceable getOLATResoucreable(PortfolioElement element) {
switch(element.getType()) {
case binder: return OresHelper.createOLATResourceableInstance(Binder.class, element.getKey());
case section: return OresHelper.createOLATResourceableInstance(Section.class, element.getKey());
case page: return OresHelper.createOLATResourceableInstance(Page.class, element.getKey());
default: return null;
}
}
private void updateCategories(OLATResourceable oresource, List<String> categories) {
List<Category> currentCategories = categoryDao.getCategories(oresource);
Map<String,Category> currentCategoryMap = new HashMap<>();
for(Category category:currentCategories) {
currentCategoryMap.put(category.getName(), category);
}
List<String> newCategories = new ArrayList<>(categories);
for(String newCategory:newCategories) {
if(!currentCategoryMap.containsKey(newCategory)) {
Category category = categoryDao.createAndPersistCategory(newCategory);
categoryDao.appendRelation(oresource, category);
}
}
for(Category currentCategory:currentCategories) {
String name = currentCategory.getName();
if(!newCategories.contains(name)) {
categoryDao.removeRelation(oresource, currentCategory);
}
}
}
@Override
public Map<Long,Long> getNumberOfComments(BinderRef binder) {
return commentDao.getNumberOfComments(binder);
}
@Override
public Map<Long,Long> getNumberOfComments(SectionRef section) {
return commentDao.getNumberOfComments(section);
}
@Override
public Map<Long,Long> getNumberOfCommentsOnOwnedPage(IdentityRef owner) {
return commentDao.getNumberOfCommentsOnOwnedPage(owner);
}
@Override
public File getPosterImageFile(BinderLight binder) {
String imagePath = binder.getImagePath();
if(StringHelper.containsNonWhitespace(imagePath)) {
File bcroot = portfolioFileStorage.getRootDirectory();
return new File(bcroot, imagePath);
}
return null;
}
@Override
public VFSLeaf getPosterImageLeaf(BinderLight binder) {
String imagePath = binder.getImagePath();
if(StringHelper.containsNonWhitespace(imagePath)) {
OlatRootFileImpl leaf = new OlatRootFileImpl("/" + imagePath, null);
if(leaf.exists()) {
return leaf;
}
}
return null;
}
@Override
public String addPosterImageForBinder(File file, String filename) {
File dir = portfolioFileStorage.generateBinderSubDirectory();
if(!StringHelper.containsNonWhitespace(filename)) {
filename = file.getName();
}
File destinationFile = new File(dir, filename);
String renamedFile = FileUtils.rename(destinationFile);
if(renamedFile != null) {
destinationFile = new File(dir, renamedFile);
}
FileUtils.copyFileToFile(file, destinationFile, false);
return portfolioFileStorage.getRelativePath(destinationFile);
}
@Override
public void removePosterImage(Binder binder) {
String imagePath = binder.getImagePath();
if(StringHelper.containsNonWhitespace(imagePath)) {
File bcroot = portfolioFileStorage.getRootDirectory();
File file = new File(bcroot, imagePath);
if(file.exists()) {
file.delete();
}
}
}
@Override
public List<Page> searchOwnedPages(IdentityRef owner, String searchString) {
List<Page> pages = pageDao.getOwnedPages(owner, searchString);
return pages;
}
@Override
public List<Page> searchDeletedPages(IdentityRef owner, String searchString) {
List<Page> pages = pageDao.getDeletedPages(owner, searchString);
return pages;
}
@Override
public Page appendNewPage(Identity owner, String title, String summary, String imagePath, PageImageAlign align, SectionRef section) {
return appendNewPage(owner, title, summary, imagePath, true, align, section);
}
private Page appendNewPage(Identity owner, String title, String summary, String imagePath, boolean editable, PageImageAlign align, SectionRef section) {
Section reloadedSection = section == null ? null : binderDao.loadSectionByKey(section.getKey());
if(reloadedSection != null && reloadedSection.getSectionStatus() == SectionStatus.notStarted) {
((SectionImpl)reloadedSection).setSectionStatus(SectionStatus.inProgress);
}
Page page = pageDao.createAndPersist(title, summary, imagePath, align, editable, reloadedSection, null);
groupDao.addMembershipTwoWay(page.getBaseGroup(), owner, PortfolioRoles.owner.name());
return page;
}
@Override
public Page getPageByKey(Long key) {
return pageDao.loadByKey(key);
}
@Override
public Page getPageByBody(PageBody body) {
return pageDao.loadByBody(body);
}
@Override
public Page getLastPage(Identity owner, boolean mandatoryBinder) {
return pageDao.getLastPage(owner, mandatoryBinder);
}
@Override
public Page updatePage(Page page, SectionRef newParentSection) {
Page updatedPage;
if(newParentSection == null) {
updatedPage = pageDao.updatePage(page);
} else {
Section currentSection = null;
if(page.getSection() != null) {
currentSection = binderDao.loadSectionByKey(page.getSection().getKey());
currentSection.getPages().remove(page);
}
Section newParent = binderDao.loadSectionByKey(newParentSection.getKey());
((PageImpl)page).setSection(newParent);
newParent.getPages().add(page);
updatedPage = pageDao.updatePage(page);
if(currentSection != null) {
binderDao.updateSection(currentSection);
}
binderDao.updateSection(newParent);
}
return updatedPage;
}
@Override
public File getPosterImage(Page page) {
String imagePath = page.getImagePath();
if(StringHelper.containsNonWhitespace(imagePath)) {
File bcroot = portfolioFileStorage.getRootDirectory();
return new File(bcroot, imagePath);
}
return null;
}
@Override
public String addPosterImageForPage(File file, String filename) {
File dir = portfolioFileStorage.generatePageSubDirectory();
File destinationFile = new File(dir, filename);
String renamedFile = FileUtils.rename(destinationFile);
if(renamedFile != null) {
destinationFile = new File(dir, renamedFile);
}
FileUtils.copyFileToFile(file, destinationFile, false);
return portfolioFileStorage.getRelativePath(destinationFile);
}
@Override
public void removePosterImage(Page page) {
String imagePath = page.getImagePath();
if(StringHelper.containsNonWhitespace(imagePath)) {
File bcroot = portfolioFileStorage.getRootDirectory();
File file = new File(bcroot, imagePath);
if(file.exists()) {
file.delete();
}
}
}
@Override
@SuppressWarnings("unchecked")
public <U extends PagePart> U appendNewPagePart(Page page, U part) {
PageBody body = pageDao.loadPageBodyByKey(page.getBody().getKey());
return (U)pageDao.persistPart(body, part);
}
@Override
@SuppressWarnings("unchecked")
public <U extends PagePart> U appendNewPagePartAt(Page page, U part, int index) {
PageBody body = pageDao.loadPageBodyByKey(page.getBody().getKey());
return (U)pageDao.persistPart(body, part, index);
}
@Override
public void removePagePart(Page page, PagePart part) {
PageBody body = pageDao.loadPageBodyByKey(page.getBody().getKey());
pageDao.removePart(body, part);
}
@Override
public void moveUpPagePart(Page page, PagePart part) {
PageBody body = pageDao.loadPageBodyByKey(page.getBody().getKey());
pageDao.moveUpPart(body, part);
}
@Override
public void moveDownPagePart(Page page, PagePart part) {
PageBody body = pageDao.loadPageBodyByKey(page.getBody().getKey());
pageDao.moveDownPart(body, part);
}
@Override
public Page removePage(Page page) {
return pageDao.removePage(page);
}
@Override
public void deletePage(Page page) {
Page reloadedPage = pageDao.loadByKey(page.getKey());
pageDao.deletePage(reloadedPage);
}
@Override
public List<PagePart> getPageParts(Page page) {
return pageDao.getParts(page.getBody());
}
@Override
@SuppressWarnings("unchecked")
public <U extends PagePart> U updatePart(U part) {
return (U)pageDao.merge(part);
}
@Override
public MediaHandler getMediaHandler(String type) {
if(mediaHandlers != null) {
for(MediaHandler handler:mediaHandlers) {
if(type.equals(handler.getType())) {
return handler;
}
}
}
return null;
}
@Override
public List<MediaHandler> getMediaHandlers() {
return new ArrayList<>(mediaHandlers);
}
@Override
public Media updateMedia(Media media) {
return mediaDao.update(media);
}
@Override
public void deleteMedia(Media media) {
mediaDao.deleteMedia(media);
}
@Override
public void updateCategories(Media media, List<String> categories) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance(Media.class, media.getKey());
updateCategories(ores, categories);
}
@Override
public List<Category> getCategories(Media media) {
OLATResourceable ores = OresHelper.createOLATResourceableInstance(Media.class, media.getKey());
return categoryDao.getCategories(ores);
}
@Override
public List<CategoryLight> getMediaCategories(IdentityRef owner) {
return categoryDao.getMediaCategories(owner);
}
@Override
public List<MediaLight> searchOwnedMedias(IdentityRef author, String searchString, List<String> tagNames) {
return mediaDao.searchByAuthor(author, searchString, tagNames);
}
@Override
public Media getMediaByKey(Long key) {
return mediaDao.loadByKey(key);
}
@Override
public List<BinderPageUsage> getUsedInBinders(MediaLight media) {
return mediaDao.usedInBinders(media);
}
@Override
public Page changePageStatus(Page page, PageStatus status) {
PageStatus currentStatus = page.getPageStatus();
Page reloadedPage = pageDao.loadByKey(page.getKey());
((PageImpl)reloadedPage).setPageStatus(status);
if(status == PageStatus.published) {
Date now = new Date();
if(reloadedPage.getInitialPublicationDate() == null) {
((PageImpl)reloadedPage).setInitialPublicationDate(now);
}
((PageImpl)reloadedPage).setLastPublicationDate(now);
Section section = reloadedPage.getSection();
// auto update the status of the evaluation form of the authors of the binder
changeAssignmentStatus(page, section, EvaluationFormSessionStatus.done);
if(section != null) {
SectionStatus sectionStatus = section.getSectionStatus();
if(currentStatus == PageStatus.closed) {
if(sectionStatus == SectionStatus.closed) {
((SectionImpl)section).setSectionStatus(SectionStatus.inProgress);
binderDao.updateSection(section);
}
} else if(sectionStatus == null || sectionStatus == SectionStatus.notStarted || sectionStatus == SectionStatus.closed) {
((SectionImpl)section).setSectionStatus(SectionStatus.inProgress);
binderDao.updateSection(section);
}
}
} else if(status == PageStatus.inRevision) {
Section section = reloadedPage.getSection();
changeAssignmentStatus(page, section, EvaluationFormSessionStatus.inProgress);
if(section != null) {
SectionStatus sectionStatus = section.getSectionStatus();
if(sectionStatus == null || sectionStatus == SectionStatus.notStarted || sectionStatus == SectionStatus.closed) {
if(sectionStatus == SectionStatus.closed) {
((SectionImpl)section).setSectionStatus(SectionStatus.inProgress);
binderDao.updateSection(section);
}
}
}
}
return pageDao.updatePage(reloadedPage);
}
/**
* Auto update the status of the evaluation form of the authors of the binder.
*
* @param page The page where the evaluation is
* @param section The section of the page
* @param newStatus The new status of the evaluation
*/
private void changeAssignmentStatus(Page page, Section section, EvaluationFormSessionStatus newStatus) {
// auto update the status of the evaluation form of the authors of the binder
Assignment assignment = assignmentDao.loadAssignment(page.getBody());
if(assignment != null && assignment.getAssignmentType() == AssignmentType.form) {
List<Identity> owners = getMembers(section.getBinder(), PortfolioRoles.owner.name());
for(Identity owner:owners) {
evaluationFormSessionDao.changeStatusOfSessionForPortfolioEvaluation(owner, page.getBody(), newStatus);
}
} else if(evaluationFormSessionDao.hasSessionForPortfolioEvaluation(page.getBody())) {
List<Identity> owners = getMembers(section.getBinder(), PortfolioRoles.owner.name());
for(Identity owner:owners) {
evaluationFormSessionDao.changeStatusOfSessionForPortfolioEvaluation(owner, page.getBody(), newStatus);
}
}
}
@Override
public Section changeSectionStatus(Section section, SectionStatus status, Identity coach) {
PageStatus newPageStatus;
if(status == SectionStatus.closed) {
newPageStatus = PageStatus.closed;
} else {
newPageStatus = PageStatus.inRevision;
}
Section reloadedSection = binderDao.loadSectionByKey(section.getKey());
List<Page> pages = reloadedSection.getPages();
for(Page page:pages) {
if(page != null) {
((PageImpl)page).setPageStatus(newPageStatus);
pageDao.updatePage(page);
}
}
((SectionImpl)reloadedSection).setSectionStatus(status);
reloadedSection = binderDao.updateSection(reloadedSection);
return reloadedSection;
}
@Override
public List<AssessmentSection> getAssessmentSections(BinderRef binder, Identity coach) {
return assessmentSectionDao.loadAssessmentSections(binder);
}
@Override
public void updateAssessmentSections(BinderRef binderRef, List<AssessmentSectionChange> changes, Identity coachingIdentity) {
Binder binder = binderDao.loadByKey(binderRef.getKey());
Map<Identity,List<AssessmentSectionChange>> assessedIdentitiesToChangesMap = new HashMap<>();
for(AssessmentSectionChange change:changes) {
List<AssessmentSectionChange> identityChanges;
if(assessedIdentitiesToChangesMap.containsKey(change.getIdentity())) {
identityChanges = assessedIdentitiesToChangesMap.get(change.getIdentity());
} else {
identityChanges = new ArrayList<>();
assessedIdentitiesToChangesMap.put(change.getIdentity(), identityChanges);
}
identityChanges.add(change);
}
for(Map.Entry<Identity,List<AssessmentSectionChange>> changesEntry:assessedIdentitiesToChangesMap.entrySet()) {
Identity assessedIdentity = changesEntry.getKey();
List<AssessmentSection> currentAssessmentSections = assessmentSectionDao.loadAssessmentSections(binder, assessedIdentity);
Set<AssessmentSection> updatedAssessmentSections = new HashSet<>(currentAssessmentSections);
List<AssessmentSectionChange> identityChanges = changesEntry.getValue();
//check update or create
for(AssessmentSectionChange change:identityChanges) {
AssessmentSection assessmentSection = change.getAssessmentSection();
for(AssessmentSection currentAssessmentSection:currentAssessmentSections) {
if(assessmentSection != null && assessmentSection.equals(currentAssessmentSection)) {
assessmentSection = currentAssessmentSection;
} else if(change.getSection().equals(currentAssessmentSection.getSection())) {
assessmentSection = currentAssessmentSection;
}
}
if(assessmentSection == null) {
assessmentSection = assessmentSectionDao
.createAssessmentSection(change.getScore(), change.getPassed(), change.getSection(), assessedIdentity);
} else {
((AssessmentSectionImpl)assessmentSection).setScore(change.getScore());
((AssessmentSectionImpl)assessmentSection).setPassed(change.getPassed());
assessmentSection = assessmentSectionDao.update(assessmentSection);
}
updatedAssessmentSections.add(assessmentSection);
}
updateAssessmentEntry(assessedIdentity, binder, updatedAssessmentSections, coachingIdentity);
}
}
private void updateAssessmentEntry(Identity assessedIdentity, Binder binder, Set<AssessmentSection> assessmentSections, Identity coachingIdentity) {
boolean allPassed = true;
int totalSectionPassed = 0;
int totalSectionClosed = 0;
BigDecimal totalScore = new BigDecimal("0.0");
AssessmentEntryStatus binderStatus = null;
for(AssessmentSection assessmentSection:assessmentSections) {
if(assessmentSection.getScore() != null) {
totalScore = totalScore.add(assessmentSection.getScore());
}
if(assessmentSection.getPassed() != null && assessmentSection.getPassed().booleanValue()) {
allPassed &= true;
totalSectionPassed++;
}
Section section = assessmentSection.getSection();
if(section.getSectionStatus() == SectionStatus.closed) {
totalSectionClosed++;
}
}
Boolean totalPassed = null;
if(totalSectionClosed == assessmentSections.size()) {
totalPassed = new Boolean(allPassed);
} else {
if(assessmentSections.size() == totalSectionPassed) {
totalPassed = Boolean.TRUE;
}
binderStatus = AssessmentEntryStatus.inProgress;
}
//order status from the entry / section
RepositoryEntry entry = binder.getEntry();
if("CourseModule".equals(entry.getOlatResource().getResourceableTypeName())) {
ICourse course = CourseFactory.loadCourse(entry);
CourseNode courseNode = course.getRunStructure().getNode(binder.getSubIdent());
if(courseNode instanceof PortfolioCourseNode) {
PortfolioCourseNode pfNode = (PortfolioCourseNode)courseNode;
ScoreEvaluation scoreEval= new ScoreEvaluation(totalScore.floatValue(), totalPassed, binderStatus, true, true, binder.getKey());
UserCourseEnvironment userCourseEnv = AssessmentHelper.createAndInitUserCourseEnvironment(assessedIdentity, course);
pfNode.updateUserScoreEvaluation(scoreEval, userCourseEnv, coachingIdentity, false);
}
} else {
OLATResource resource = ((BinderImpl)binder.getTemplate()).getOlatResource();
RepositoryEntry referenceEntry = repositoryService.loadByResourceKey(resource.getKey());
AssessmentEntry assessmentEntry = assessmentService
.getOrCreateAssessmentEntry(assessedIdentity, null, binder.getEntry(), binder.getSubIdent(), referenceEntry);
assessmentEntry.setScore(totalScore);
assessmentEntry.setPassed(totalPassed);
assessmentEntry.setAssessmentStatus(binderStatus);
assessmentService.updateAssessmentEntry(assessmentEntry);
}
}
@Override
public AssessmentEntryStatus getAssessmentStatus(Identity assessedIdentity, BinderRef binderRef) {
Binder binder = binderDao.loadByKey(binderRef.getKey());
RepositoryEntry entry = binder.getEntry();
AssessmentEntryStatus status = null;
if("CourseModule".equals(entry.getOlatResource().getResourceableTypeName())) {
ICourse course = CourseFactory.loadCourse(entry);
CourseNode courseNode = course.getRunStructure().getNode(binder.getSubIdent());
if(courseNode instanceof PortfolioCourseNode) {
PortfolioCourseNode pfNode = (PortfolioCourseNode)courseNode;
UserCourseEnvironment userCourseEnv = AssessmentHelper.createAndInitUserCourseEnvironment(assessedIdentity, course);
AssessmentEvaluation eval = pfNode.getUserScoreEvaluation(userCourseEnv);
status = eval.getAssessmentStatus();
}
} else {
OLATResource resource = ((BinderImpl)binder.getTemplate()).getOlatResource();
RepositoryEntry referenceEntry = repositoryService.loadByResourceKey(resource.getKey());
AssessmentEntry assessmentEntry = assessmentService
.getOrCreateAssessmentEntry(assessedIdentity, null, binder.getEntry(), binder.getSubIdent(), referenceEntry);
status = assessmentEntry.getAssessmentStatus();
}
return status;
}
@Override
public void setAssessmentStatus(Identity assessedIdentity, BinderRef binderRef, AssessmentEntryStatus status, Identity coachingIdentity) {
Boolean fullyAssessed = Boolean.FALSE;
if(status == AssessmentEntryStatus.done) {
fullyAssessed = Boolean.TRUE;
}
Binder binder = binderDao.loadByKey(binderRef.getKey());
RepositoryEntry entry = binder.getEntry();
if("CourseModule".equals(entry.getOlatResource().getResourceableTypeName())) {
ICourse course = CourseFactory.loadCourse(entry);
CourseNode courseNode = course.getRunStructure().getNode(binder.getSubIdent());
if(courseNode instanceof PortfolioCourseNode) {
PortfolioCourseNode pfNode = (PortfolioCourseNode)courseNode;
UserCourseEnvironment userCourseEnv = AssessmentHelper.createAndInitUserCourseEnvironment(assessedIdentity, course);
AssessmentEvaluation eval = pfNode.getUserScoreEvaluation(userCourseEnv);
ScoreEvaluation scoreEval= new ScoreEvaluation(eval.getScore(), eval.getPassed(), status, true, fullyAssessed, binder.getKey());
pfNode.updateUserScoreEvaluation(scoreEval, userCourseEnv, coachingIdentity, false);
}
} else {
OLATResource resource = ((BinderImpl)binder.getTemplate()).getOlatResource();
RepositoryEntry referenceEntry = repositoryService.loadByResourceKey(resource.getKey());
AssessmentEntry assessmentEntry = assessmentService
.getOrCreateAssessmentEntry(assessedIdentity, null, binder.getEntry(), binder.getSubIdent(), referenceEntry);
assessmentEntry.setFullyAssessed(fullyAssessed);
assessmentEntry.setAssessmentStatus(status);
assessmentService.updateAssessmentEntry(assessmentEntry);
}
}
}