/** * <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.portfolio.manager; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.olat.basesecurity.IdentityRef; import org.olat.collaboration.CollaborationTools; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.services.tagging.manager.TaggingManager; import org.olat.core.commons.services.tagging.model.Tag; import org.olat.core.id.Identity; import org.olat.core.id.IdentityEnvironment; import org.olat.core.id.OLATResourceable; import org.olat.core.id.Roles; import org.olat.core.logging.AssertException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.coordinate.Coordinator; import org.olat.core.util.coordinate.SyncerCallback; import org.olat.core.util.vfs.VFSContainer; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.assessment.AssessmentManager; import org.olat.course.assessment.manager.AssessmentNotificationsHandler; import org.olat.course.nodes.CourseNode; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironmentImpl; import org.olat.group.BusinessGroup; import org.olat.group.DeletableGroupData; import org.olat.modules.assessment.AssessmentService; import org.olat.modules.assessment.model.AssessmentEntryStatus; import org.olat.modules.webFeed.portfolio.LiveBlogArtefactHandler; import org.olat.portfolio.PortfolioModule; import org.olat.portfolio.model.EPFilterSettings; import org.olat.portfolio.model.artefacts.AbstractArtefact; import org.olat.portfolio.model.structel.EPMapShort; import org.olat.portfolio.model.structel.EPPage; import org.olat.portfolio.model.structel.EPStructureElement; import org.olat.portfolio.model.structel.EPStructuredMap; import org.olat.portfolio.model.structel.EPTargetResource; import org.olat.portfolio.model.structel.ElementType; import org.olat.portfolio.model.structel.PortfolioStructure; import org.olat.portfolio.model.structel.PortfolioStructureMap; import org.olat.portfolio.model.structel.PortfolioStructureRef; import org.olat.properties.NarrowedPropertyManager; import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.repository.manager.RepositoryEntryDAO; import org.olat.resource.OLATResource; import org.olat.search.SearchResults; import org.olat.search.model.AbstractOlatDocument; import org.olat.search.model.ResultDocument; import org.olat.search.service.indexer.identity.PortfolioArtefactIndexer; import org.olat.search.service.searcher.SearchClient; import org.olat.user.UserDataDeletable; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * * Description:<br> * Manager for common used tasks for ePortfolio. Should be used for all calls * from controllers. will itself use all other managers to * manipulate artefacts or structureElements and policies. * * <P> * Initial Date: 11.06.2010 <br> * * @author Roman Haag, roman.haag@frentix.com, http://www.frentix.com */ @Service("epFrontendManager") public class EPFrontendManager implements UserDataDeletable, DeletableGroupData { private static final OLog log = Tracing.createLoggerFor(EPFrontendManager.class); @Autowired private Coordinator coordinator; @Autowired private EPArtefactManager artefactManager; @Autowired private EPStructureManager structureManager; @Autowired private TaggingManager taggingManager; @Autowired private AssessmentNotificationsHandler assessmentNotificationsHandler; @Autowired private DB dbInstance; @Autowired private SearchClient searchClient; @Autowired private EPSettingsManager settingsManager; @Autowired private EPPolicyManager policyManager; @Autowired private UserManager userManager; @Autowired private PortfolioModule portfolioModule; @Autowired private RepositoryEntryDAO repositoryEntryDao; @Autowired private AssessmentService assessmentService; /** * Check if a user has any kind of EP v1 resources: artefacts, a owned map or a shared map * @param identity * @return true: yes, has some EP v1 stuff; false: has no EP v1 resources */ public boolean hasMapOrArtefact(Identity identity) { return artefactManager.hasArtefactPool(identity) || structureManager.hasMap(identity) || structureManager.hasStructureElementsFromOthersWithoutPublic(identity); } /** * Create and persist an artefact of the given type * * @param type * @return The persisted artefact */ public AbstractArtefact createAndPersistArtefact(Identity identity, String type) { return artefactManager.createAndPersistArtefact(identity, type); } /** * Persists the artefact and returns the new version * * @param artefact * @return The last version of the artefact */ public AbstractArtefact updateArtefact(AbstractArtefact artefact) { return artefactManager.updateArtefact(artefact); } /** * delete an artefact and also its vfs-artefactContainer * all used tags will also be deleted. * @param artefact */ public void deleteArtefact(AbstractArtefact artefact) { List<PortfolioStructure> linksToArtefact = structureManager.getAllReferencesForArtefact(artefact); for (PortfolioStructure portfolioStructure : linksToArtefact) { structureManager.removeArtefactFromStructure(artefact, portfolioStructure); } // load again as session might be closed between artefact = artefactManager.loadArtefactByKey(artefact.getKey()); artefactManager.deleteArtefact(artefact); } @Override public void deleteUserData(Identity identity, String newDeletedUserName, File archivePath) { deleteUsersArtefacts(identity); List<PortfolioStructure> userPersonalMaps = getStructureElementsForUser(identity, ElementType.DEFAULT_MAP, ElementType.STRUCTURED_MAP); for (PortfolioStructure portfolioStructure : userPersonalMaps) { deletePortfolioStructure(portfolioStructure); } } @Override public boolean deleteGroupDataFor(BusinessGroup group) { final NarrowedPropertyManager npm = NarrowedPropertyManager.getInstance(group); final Property mapKeyProperty = npm.findProperty(null, null, CollaborationTools.PROP_CAT_BG_COLLABTOOLS, CollaborationTools.KEY_PORTFOLIO); if (mapKeyProperty != null) { final Long mapKey = mapKeyProperty.getLongValue(); final String version = mapKeyProperty.getStringValue(); if(!"2".equals(version)) { final PortfolioStructure map = loadPortfolioStructureByKey(mapKey); if(map != null) { deletePortfolioStructure(map); } } return true; } return false; } /** * delete all artefacts from this users including used tags for them * @param ident */ public void deleteUsersArtefacts(Identity ident){ List<AbstractArtefact> userArtefacts = artefactManager.getArtefactPoolForUser(ident); if (userArtefacts != null){ for (AbstractArtefact abstractArtefact : userArtefacts) { deleteArtefact(abstractArtefact); } } } public boolean isArtefactClosed(AbstractArtefact artefact) { return artefactManager.isArtefactClosed(artefact); } public PortfolioStructure removeArtefactFromStructure(AbstractArtefact artefact, PortfolioStructure structure) { return structureManager.removeArtefactFromStructure(artefact, structure); } /** * Create and persist a link between a structure element and an artefact. * * @param author The author of the link * @param artefact The artefact to link * @param structure The structure element * @return The link */ public boolean addArtefactToStructure(Identity author, AbstractArtefact artefact, PortfolioStructure structure) { return structureManager.addArtefactToStructure(author, artefact, structure); } /** * move artefact from old to new structure * do so by removing and re-adding to new target * @param artefact * @param oldParStruct * @param newParStruct * @return true if adding was successful */ public boolean moveArtefactFromStructToStruct(AbstractArtefact artefact, PortfolioStructure oldParStruct, PortfolioStructure newParStruct) { return structureManager.moveArtefactFromStructToStruct(artefact, oldParStruct, newParStruct); } public boolean moveArtefactInStruct(AbstractArtefact artefact, PortfolioStructure parStruct, int position) { return structureManager.moveArtefactInStruct(artefact, parStruct, position); } /** * move a structure to a new parent-structure and removes old link * @param structToBeMvd * @param oldParStruct * @param newParStruct * @return true if no exception occured */ public boolean moveStructureToNewParentStructure(PortfolioStructure structToBeMvd, PortfolioStructure oldParStruct, PortfolioStructure newParStruct, int destinationPos){ return structureManager.moveStructureToNewParentStructure(structToBeMvd, oldParStruct, newParStruct, destinationPos); } /** * move a structures order within the same parent, allows manual sorting. * @param structToBeMvd * @param destinationPos where it should be placed * @return true if it went ok, false otherwise */ public boolean moveStructureToPosition(PortfolioStructure structToBeMvd, int destinationPos){ return structureManager.reOrderStructures(loadStructureParent(structToBeMvd), structToBeMvd, destinationPos); } /** * set the reflexion for the link structureElement <-> artefact * this can be a different reflexion than the one of the artefact. * Reflects why the artefact was added to this structure! * @param artefact * @param structure * @param reflexion * @return */ public boolean setReflexionForArtefactToStructureLink(AbstractArtefact artefact, PortfolioStructure structure, String reflexion){ return structureManager.setReflexionForArtefactToStructureLink(artefact, structure, reflexion); } /** * get the reflexion set on the link structureElement <-> artefact * this can be a different reflexion than the one of the artefact. * Reflects why the artefact was added to this structure! * @param artefact * @param structure * @return String reflexion */ public String getReflexionForArtefactToStructureLink(AbstractArtefact artefact, PortfolioStructure structure){ return structureManager.getReflexionForArtefactToStructureLink(artefact, structure); } /** * counts amount of artefact in all structures and every child element * @param structure * @return */ public int countArtefactsRecursively(PortfolioStructure structure) { return structureManager.countArtefactsRecursively(structure, 0); } public int countArtefactsInMap(PortfolioStructureMap map) { return structureManager.countArtefactsRecursively(map); } /** * looks if the given artefact exists in the PortfolioStructure * @param artefact * @param structure * @return */ public boolean isArtefactInStructure(AbstractArtefact artefact, PortfolioStructure structure){ return structureManager.isArtefactInStructure(artefact, structure); } /** * load all artefacts with given businesspath. * setting an Identity to restrict to is optional. * this mostly is just to lookup for existence of already collected artefacts from same source * @param businessPath * @param author (optional) * @return */ public List<AbstractArtefact> loadArtefactsByBusinessPath(String businessPath, Identity author){ return artefactManager.loadArtefactsByBusinessPath(businessPath, author); } public Map<String,Long> getNumOfArtefactsByStartingBusinessPath(String businessPath, IdentityRef author){ return artefactManager.loadNumOfArtefactsByStartingBusinessPath(businessPath, author); } /** * List artefacts for indexing * @param author (optional) * @param firstResult (optional) * @param maxResults (optional) * @return */ public List<AbstractArtefact> getArtefacts(Identity author, int firstResult, int maxResults) { return artefactManager.getArtefacts(author, null, firstResult, maxResults); } /** * Load the artefact by its primary key * * @param key The primary key * @return The artefact or null if nothing found */ public AbstractArtefact loadArtefactByKey(Long key) { return artefactManager.loadArtefactByKey(key); } /** * get the users choice of attributes or a default * * @return */ public Map<String, Boolean> getArtefactAttributeConfig(Identity ident) { return settingsManager.getArtefactAttributeConfig(ident); } /** * persist the users chosen attributes to show as a property * * @param ident * @param artAttribConfig */ public void setArtefactAttributeConfig(Identity ident, Map<String, Boolean> artAttribConfig) { settingsManager.setArtefactAttributeConfig(ident, artAttribConfig); } /** * get all persisted filters from a given user * @param ident * @return filtersettings or list with an empty filter, if none were found */ public List<EPFilterSettings> getSavedFilterSettings(Identity ident){ return settingsManager.getSavedFilterSettings(ident); } /** * persist users filter settings as property, only save such with a name * @param ident * @param filterList */ public void setSavedFilterSettings(Identity ident, List<EPFilterSettings> filterList){ settingsManager.setSavedFilterSettings(ident, filterList); } /** * remove a given filter from users list * @param ident * @param filterName */ public void deleteFilterFromUsersList(Identity ident, String filterID){ settingsManager.deleteFilterFromUsersList(ident, filterID); } /** * get the last selected PortfolioStructure of this user * @param ident Identity * @return the loaded PortfolioStructure */ public PortfolioStructure getUsersLastUsedPortfolioStructure (Identity ident){ Long structKey = settingsManager.getUsersLastUsedPortfolioStructureKey(ident); if (structKey != null) { PortfolioStructure struct = structureManager.loadPortfolioStructureByKey(structKey); return struct; } return null; } /** * get the users prefered viewing mode for artefacts (either table / preview) * @param ident * @return */ public String getUsersPreferedArtefactViewMode(Identity ident, String context){ return settingsManager.getUsersPreferedArtefactViewMode(ident, context); } /** * persist the users prefered viewing mode for artefacts (either table / preview) * @param ident * @param preferedMode */ public void setUsersPreferedArtefactViewMode(Identity ident, String preferedMode, String context){ settingsManager.setUsersPreferedArtefactViewMode(ident, preferedMode, context); } /** * persist the last uses PortfolioStructure to use it later on * @param ident Identity * @param struct */ public void setUsersLastUsedPortfolioStructure(Identity ident, PortfolioStructure struct){ settingsManager.setUsersLastUsedPortfolioStructure(ident, struct); } /** * returns an array of tags for given artefact * * @param artefact * @return null if none are found */ public List<String> getArtefactTags(AbstractArtefact artefact) { return artefactManager.getArtefactTags(artefact); } /** * add a tag to an artefact (will save a tag pointing to this artefact) * * @param identity * @param artefact * @param tag */ public void setArtefactTag(Identity identity, AbstractArtefact artefact, String tag) { artefactManager.setArtefactTag(identity, artefact, tag); } /** * add a List of tags to an artefact * * @param identity * @param artefact * @param tags */ public void setArtefactTags(Identity identity, AbstractArtefact artefact, List<String> tags) { artefactManager.setArtefactTags(identity, artefact, tags); } /** * get all maps wherein (or in sub-structures) the given artefact is linked. * * @param artefact * @return */ public List<PortfolioStructure> getReferencedMapsForArtefact(AbstractArtefact artefact) { return structureManager.getReferencedMapsForArtefact(artefact); } /** * get all artefacts for the given identity this represents the artefact pool * * @param ident * @return */ public List<AbstractArtefact> getArtefactPoolForUser(Identity ident) { return artefactManager.getArtefactPoolForUser(ident); } public EPArtefactTagCloud getArtefactsAndTagCloud(Identity identity, List<String> tags) { return artefactManager.getArtefactsAndTagCloud(identity, tags); } /** * filter the provided list of artefacts with different filters * * @param allArtefacts the list to manipulate on * @param filterSettings Settings for the filter to work on * @return */ public List<AbstractArtefact> filterArtefactsByFilterSettings(EPFilterSettings filterSettings, Identity identity, Roles roles) { List<Long> artefactKeys = fulltextSearchAfterArtefacts(filterSettings, identity, roles); if(artefactKeys == null || artefactKeys.isEmpty()) { List<AbstractArtefact> allArtefacts = artefactManager.getArtefactPoolForUser(identity); return artefactManager.filterArtefactsByFilterSettings(allArtefacts, filterSettings); } List<AbstractArtefact> artefacts = artefactManager.getArtefacts(identity, artefactKeys, 0, 500); // remove the text-filter when the lucene-search got some results before EPFilterSettings settings = filterSettings.cloneAfterFullText(); return artefactManager.filterArtefactsByFilterSettings(artefacts, settings); } private List<Long> fulltextSearchAfterArtefacts(EPFilterSettings filterSettings, Identity identity, Roles roles) { String query = filterSettings.getTextFilter(); if (StringHelper.containsNonWhitespace(query)) { try { List<String> queries = new ArrayList<String>(); appendAnd(queries, AbstractOlatDocument.RESERVED_TO, ":\"", identity.getKey().toString(), "\""); appendAnd(queries, "(", AbstractOlatDocument.DOCUMENTTYPE_FIELD_NAME, ":(", PortfolioArtefactIndexer.TYPE, "*))"); SearchResults searchResults = searchClient.doSearch(query, queries, identity, roles, 0, 1000, false); List<Long> keys = new ArrayList<Long>(); if (searchResults != null) { String marker = AbstractArtefact.class.getSimpleName(); for (ResultDocument doc : searchResults.getList()) { String businessPath = doc.getResourceUrl(); int start = businessPath.indexOf(marker); if (start > 0) { start += marker.length() + 1; int stop = businessPath.indexOf(']', start); if (stop < businessPath.length()) { String keyStr = businessPath.substring(start, stop); try { keys.add(Long.parseLong(keyStr)); } catch (Exception e) { log.error("Not a primary key: " + keyStr, e); } } } } } return keys; } catch (Exception e) { log.error("", e); return Collections.emptyList(); } } else return Collections.emptyList(); } private void appendAnd(List<String> queries, String... strings) { StringBuilder query = new StringBuilder(); for(String string:strings) { query.append(string); } if(query.length() > 0) { queries.add(query.toString()); } } /** * returns defined amount of users mostly used tags, sorted by occurrence of tag * @param ident * @param amount nr of tags to return, if 0: the default (5) will be * returned, if -1: you will get all * @return a combined map with tags including occurrence and tag * format: "house (7), house" */ public Map<String, String> getUsersMostUsedTags(Identity ident, Integer amount) { amount = (amount == 0) ? 5 : amount; List<String> outp = new ArrayList<String>(); Map<String, String> res = new HashMap<String, String>(); List<Map<String, Integer>> bla = taggingManager.getUserTagsWithFrequency(ident); for (Map<String, Integer> map : bla) { String caption = map.get("tag") + " (" + map.get("nr") + ")"; outp.add(caption); res.put(caption, String.valueOf(map.get("tag"))); if (amount == res.size()) break; } return res; } /** * get all tags a user owns, ordered and without duplicates * @param ident * @return */ public List<String> getUsersTags(Identity ident) { return taggingManager.getUserTagsAsString(ident); } /** * get all tags restricted to Artefacts a user owns, ordered and without duplicates * @param ident * @return */ public List<String> getUsersTagsOfArtefactType(Identity ident) { return taggingManager.getUserTagsOfTypeAsString(ident, AbstractArtefact.class.getSimpleName()); } /** * lookup resources for a given tags * @param tagList * @return */ public Set<OLATResourceable> getResourcesByTags(List<Tag> tagList) { return taggingManager.getResourcesByTags(tagList); } /** * get all tags for a given resource * @param ores * @return */ public List<Tag> loadTagsForResource(OLATResourceable ores) { return taggingManager.loadTagsForResource(ores, null, null); } /** * sync map with its former source (template) */ public boolean synchronizeStructuredMapToUserCopy(PortfolioStructureMap map) { if(map == null) return false; final EPStructuredMap userMap = (EPStructuredMap)map; Boolean synched = coordinator.getSyncer().doInSync(map.getOlatResource(), new SyncerCallback<Boolean>() { public Boolean execute() { if (userMap.getStructuredMapSource() == null) { return Boolean.FALSE; } // need to reload it, I don't know why Long templateKey = userMap.getStructuredMapSource().getKey(); userMap.setLastSynchedDate(new Date()); PortfolioStructure template = structureManager.loadPortfolioStructureByKey(templateKey); structureManager.syncStructureRecursively(template, userMap, true); return Boolean.TRUE; } }); return synched.booleanValue(); } /** * Assign a structure map to user. In other words, make a copy of the template * and set the user as an author. * * @param identity * @param portfolioStructureStructuredMapTemplate */ public PortfolioStructureMap assignStructuredMapToUser(final Identity identity, final PortfolioStructureMap mapTemplate, final RepositoryEntry courseEntry, String targetSubPath, final String targetBusinessPath, final Date deadline) { // doInSync is here to check for nested doInSync exception in first place final OLATResource ores = courseEntry.getOlatResource(); final String subPath = targetSubPath; PortfolioStructureMap map = coordinator.getSyncer().doInSync(mapTemplate.getOlatResource(), new SyncerCallback<PortfolioStructureMap>() { @Override public PortfolioStructureMap execute() { PortfolioStructureMap template = (PortfolioStructureMap)structureManager.loadPortfolioStructureByKey(mapTemplate.getKey()); String title = template.getTitle(); String description = template.getDescription(); PortfolioStructureMap copy = structureManager .createPortfolioStructuredMap(template, identity, title, description, ores, subPath, targetBusinessPath); if(copy instanceof EPStructuredMap) { ((EPStructuredMap)copy).setDeadLine(deadline); } structureManager.copyStructureRecursively(template, copy, true); RepositoryEntry referenceEntry = repositoryEntryDao.loadByResourceKey(template.getOlatResource().getKey()); assessmentService.updateAssessmentEntry(identity, courseEntry, targetSubPath, referenceEntry, AssessmentEntryStatus.inProgress); return copy; } }); return map; } /** * Low level function to copy the structure of elements, with or without the artefacts * @param source * @param target * @param withArtefacts */ public void copyStructureRecursively(PortfolioStructure source, PortfolioStructure target, boolean withArtefacts) { structureManager.copyStructureRecursively(source, target, withArtefacts); } /** * Return the structure elements of the given type without permission control. Need this for indexing. * @param firstResult * @param maxResults * @param type * @return */ public List<PortfolioStructure> getStructureElements(int firstResult, int maxResults, ElementType... type) { return structureManager.getStructureElements(firstResult, maxResults, type); } /** * get all Structure-Elements linked to identity over a security group (owner) * * @param ident * @return */ public List<PortfolioStructure> getStructureElementsForUser(Identity identity, ElementType... type) { return structureManager.getStructureElementsForUser(identity, type); } /** * Get all Structure-Elements linked which the identity can see over a policy, * * @param ident The identity which what see maps * @param chosenOwner Limit maps from this identity * @param type Limit maps to this or these types * @return */ public List<PortfolioStructure> getStructureElementsFromOthers(final Identity ident, final Identity chosenOwner, final ElementType... type) { return structureManager.getStructureElementsFromOthersLimited(ident, chosenOwner, 0, 0, type); } /** * Get part of the Structure-Elements linked which the identity can see over a policy. * The range of elements returned is specified by limitFrom and limitTo (used for paging) * * @param ident The identity which what see maps * @param chosenOwner Limit maps from this identity * @param limitFrom Limit maps * @param limitTo Limit maps * @param type Limit maps to this or these types * @return */ public List<PortfolioStructure> getStructureElementsFromOthers(final Identity ident, final Identity chosenOwner, int limitFrom, int limitTo, final ElementType... type) { return structureManager.getStructureElementsFromOthersLimited(ident, chosenOwner, limitFrom, limitTo, type); } /** * Get the number of all Structure-Elements linked which the identity can see over a policy, * * @param ident The identity which what see maps * @param chosenOwner Limit maps from this identity * @param type Limit maps to this or these types * @return */ public int countStructureElementsFromOthers(final Identity ident, final Identity chosenOwner, final ElementType... types) { return structureManager.countStructureElementsFromOthers(ident, chosenOwner, types); } /** * Get all Structure-Elements linked which the identity can see over a policy, * WITHOUT those that are public to all OLAT users ( GROUP_OLATUSERS ) * !! this should be used, to save performance when there are a lot of public shared maps!! * @param ident The identity which what see maps * @param chosenOwner Limit maps from this identity * @param type Limit maps to this or these types * @return */ public List<PortfolioStructure> getStructureElementsFromOthersWithoutPublic(IdentityRef ident, IdentityRef choosenOwner, ElementType... types){ return structureManager.getStructureElementsFromOthersWithoutPublic(ident, choosenOwner, types); } /** * Return the list of artefacts glued to this structure element * @param structure * @return A list of artefacts */ public List<AbstractArtefact> getArtefacts(PortfolioStructure structure) { return structureManager.getArtefacts(structure); } /** * FXOLAT-431 * * @param map * @return * public List<AbstractArtefact> getAllArtefactsInMap(EPAbstractMap map){ return structureManager.getAllArtefactsInMap(map); } */ /** * get statistics about how much of the required (min, equal) collect-restrictions have been fulfilled. * * @param structure * @return array with "done" at 0 and "to be done" at 1, or "null" if no restrictions apply */ public String[] getRestrictionStatistics(PortfolioStructure structure) { Integer[] stats = structureManager.getRestrictionStatistics(structure); if(stats == null) { return null; } else { return new String[]{stats[0].toString(), stats[1].toString()}; } } /** * same as getRestrictionStatistics(PortfolioStructure structure) but recursively for a map. * get statistics about how much of the required (min, equal) collect-restrictions have been fulfilled. * * @param structure * @return array with "done" at 0 and "to be done" at 1, or "null" if no restrictions apply */ public String[] getRestrictionStatisticsOfMap(final PortfolioStructureMap structure) { Integer[] stats = structureManager.getRestrictionStatisticsOfMap(structure, 0, 0); return new String[]{stats[0].toString(), stats[1].toString()}; } /** * Check the collect restriction against the structure element * @param structure * @return */ public boolean checkCollectRestriction(PortfolioStructure structure) { return structureManager.checkCollectRestriction(structure); } public boolean checkCollectRestrictionOfMap(PortfolioStructureMap structure) { return checkAllCollectRestrictionRec(structure); } protected boolean checkAllCollectRestrictionRec(PortfolioStructure structure) { boolean allOk = structureManager.checkCollectRestriction(structure); List<PortfolioStructure> children = structureManager.loadStructureChildren(structure); for(PortfolioStructure child:children) { allOk &= checkAllCollectRestrictionRec(child); } return allOk; } /** * Create a map for a user * @param root * @param identity * @param title * @param description * @return */ public PortfolioStructureMap createAndPersistPortfolioDefaultMap(Identity identity, String title, String description) { PortfolioStructureMap map = structureManager.createPortfolioDefaultMap(identity, title, description); structureManager.savePortfolioStructure(map); return map; } /** * Create a map for a group * @param root * @param group * @param title * @param description * @return */ public PortfolioStructureMap createAndPersistPortfolioDefaultMap(String title, String description) { PortfolioStructureMap map = structureManager.createPortfolioDefaultMap(title, description); structureManager.savePortfolioStructure(map); return map; } /** * Create a structured map, based on template. * * @param identity The author/owner of the map * @param title * @param description * @return The structure element */ public PortfolioStructureMap createAndPersistPortfolioStructuredMap(PortfolioStructureMap template, Identity identity, String title, String description, OLATResourceable targetOres, String targetSubPath, String targetBusinessPath) { PortfolioStructureMap map = structureManager.createPortfolioStructuredMap(template, identity, title, description, targetOres, targetSubPath, targetBusinessPath); structureManager.savePortfolioStructure(map); return map; } /** * create a structure-element * @param root * @param title * @param description * @return */ public PortfolioStructure createAndPersistPortfolioStructureElement(PortfolioStructure root, String title, String description) { EPStructureElement newStruct = (EPStructureElement) structureManager.createPortfolioStructure(root, title, description); if (root != null) structureManager.addStructureToStructure(root, newStruct, -1); structureManager.savePortfolioStructure(newStruct); return newStruct; } /** * create a page * @param root * @param title * @param description * @return */ public PortfolioStructure createAndPersistPortfolioPage(PortfolioStructure root, String title, String description) { EPPage newPage = (EPPage) structureManager.createPortfolioPage(root, title, description); if (root != null) structureManager.addStructureToStructure(root, newPage, -1); structureManager.savePortfolioStructure(newPage); return newPage; } /** * This method is reserved to the repository. It removes the template * completely * @param pStruct */ public void deletePortfolioMapTemplate(OLATResourceable res) { structureManager.deletePortfolioMapTemplate(res); } /** * delete a portfoliostructure recursively with its childs * @param pStruct */ public void deletePortfolioStructure(PortfolioStructure pStruct) { structureManager.removeStructureRecursively(pStruct); } /** * save or update a structure * @param pStruct */ public void savePortfolioStructure(PortfolioStructure pStruct) { structureManager.savePortfolioStructure(pStruct); } /** * Number of children */ public int countStructureChildren(PortfolioStructure structure) { return structureManager.countStructureChildren(structure); } /** * Load a protfolio structure by its resource * @param ores * @return */ public PortfolioStructure loadPortfolioStructure(OLATResourceable ores) { return structureManager.loadPortfolioStructure(ores); } /** * Load a protfolio structure by its resourceable id * @param ores * @return */ public EPMapShort loadMapShortByResourceId(Long resId) { return structureManager.loadMapShortByResourceId(resId); } /** * Load a portfolio structure by its primary key. DON'T USE THIS METHOD * TO RELOAD AN OBJECT. If you want do this, use the method * reloadPortfolioStructure(PortfolioStructure structure) * @param key cannot be null * @return The structure element or null if not found */ public PortfolioStructure loadPortfolioStructureByKey(Long key){ return structureManager.loadPortfolioStructureByKey(key); } public PortfolioStructure loadPortfolioStructureByKey(PortfolioStructureRef ref){ return structureManager.loadPortfolioStructureByKey(ref.getKey()); } /** * Reload a portfolio structure * @param structure cannot be null * @return The reloaded structure element */ public PortfolioStructure reloadPortfolioStructure(PortfolioStructure structure){ return structureManager.reloadPortfolioStructure(structure); } /** * Load the OLAT resource with the primary of the structure element * @param key cannot be null * @return The resource or null if not found */ public OLATResource loadOlatResourceFromByKey(Long key) { return structureManager.loadOlatResourceFromStructureElByKey(key); } /** * Retrieve the parent of the structure * @param structure * @return */ public PortfolioStructure loadStructureParent(PortfolioStructureRef structure) { return structureManager.loadStructureParent(structure); } /** * Retrieve the children structures * @param structure * @return */ public List<PortfolioStructure> loadStructureChildren(PortfolioStructure structure) { return structureManager.loadStructureChildren(structure); } /** * * @param structure * @param firstResult * @param maxResults * @return */ public List<PortfolioStructure> loadStructureChildren(PortfolioStructure structure, int firstResult, int maxResults) { return structureManager.loadStructureChildren(structure, firstResult, maxResults); } public PortfolioStructureMap loadPortfolioStructureMap(Identity identity, PortfolioStructureMap template, OLATResourceable targetOres, String targetSubPath, String targetBusinessPath){ //sync the map with the template on opening it in gui, not on loading! return structureManager.loadPortfolioStructuredMap(identity, template, targetOres, targetSubPath, targetBusinessPath); } /** * * @param identity Cannot be null * @param targetOres Cannot be null * @param targetSubPath * @param targetBusinessPath * @return */ public List<PortfolioStructureMap> loadPortfolioStructureMaps(Identity identity, OLATResourceable targetOres, String targetSubPath, String targetBusinessPath){ //sync the map with the template on opening it in gui, not on loading! return structureManager.loadPortfolioStructuredMaps(identity, targetOres, targetSubPath, targetBusinessPath); } /** * get the "already in use" state of a structuredMapTemplate * @param template * @param targetOres * @param targetSubPath * @param targetBusinessPath * @return */ public boolean isTemplateInUse(PortfolioStructureMap template, OLATResourceable targetOres, String targetSubPath, String targetBusinessPath) { return structureManager.isTemplateInUse(template, targetOres, targetSubPath, targetBusinessPath); } /** * get root vfs-container where artefact file-system data is persisted * @return */ public VFSContainer getArtefactsRoot(){ return artefactManager.getArtefactsRoot(); } /** * get vfs-container of a specific artefact * @param artefact * @return */ public VFSContainer getArtefactContainer(AbstractArtefact artefact) { return artefactManager.getArtefactContainer(artefact); } /** * get a temporary folder to store files while in wizzard * @param ident * @return */ public VFSContainer getArtefactsTempContainer(Identity ident){ return artefactManager.getArtefactsTempContainer(ident); } /** * as large fulltext-content of an artefact is persisted on filesystem, use this method to get fulltext * * @param artefact * @return */ public String getArtefactFullTextContent(AbstractArtefact artefact){ return artefactManager.getArtefactFullTextContent(artefact); } /** * Check if the identity is the owner of this portfolio resource. * @param identity * @param ores * @return */ public boolean isMapOwner(Identity identity, OLATResourceable ores) { return structureManager.isMapOwner(identity, ores); } public boolean isMapOwner(Identity identity, Long mapKey) { return structureManager.isMapOwner(identity, mapKey); } /** * Check if the identity is owner of the portfolio resource or * in a valid policy. * @param identity * @param ores * @return */ public boolean isMapVisible(IdentityRef identity, OLATResourceable ores) { return structureManager.isMapVisible(identity, ores); } public boolean isMapShared(PortfolioStructureMap map) { return isMapShared(map.getOlatResource()); } public boolean isMapShared(OLATResource resource) { return policyManager.isMapShared(resource); } /** * Return a list of wrapper containing the read policies of the map * @param map */ public List<EPMapPolicy> getMapPolicies(PortfolioStructureMap map) { return policyManager.getMapPolicies(map); } /** * Update the map policies of a map. The missing policies are deleted! * @param map * @param policyWrappers */ public PortfolioStructureMap updateMapPolicies(PortfolioStructureMap map, List<EPMapPolicy> policyWrappers) { return policyManager.updateMapPolicies(map, policyWrappers); } /** * submit and close a structured map from a portfolio task * @param map */ public void submitMap(PortfolioStructureMap map) { submitMap(map, true); } private void submitMap(PortfolioStructureMap map, boolean logActivity) { if(!(map instanceof EPStructuredMap)) return;//add an exception EPStructuredMap submittedMap = (EPStructuredMap)map; structureManager.submitMap(submittedMap); EPTargetResource resource = submittedMap.getTargetResource(); OLATResourceable courseOres = resource.getOLATResourceable(); ICourse course = CourseFactory.loadCourse(courseOres); AssessmentManager am = course.getCourseEnvironment().getAssessmentManager(); CourseNode courseNode = course.getRunStructure().getNode(resource.getSubPath()); List<Identity> owners = policyManager.getOwners(submittedMap); for(Identity owner:owners) { if (courseNode != null) { // courseNode might have been deleted meanwhile IdentityEnvironment ienv = new IdentityEnvironment(); ienv.setIdentity(owner); UserCourseEnvironment uce = new UserCourseEnvironmentImpl(ienv, course.getCourseEnvironment()); if(logActivity) { am.incrementNodeAttempts(courseNode, owner, uce); } else { am.incrementNodeAttemptsInBackground(courseNode, owner, uce); } RepositoryEntry referenceEntry = courseNode.getReferencedRepositoryEntry(); RepositoryEntry courseEntry = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry(); assessmentService.updateAssessmentEntry(owner, courseEntry, courseNode.getIdent(), referenceEntry, AssessmentEntryStatus.inReview); } assessmentNotificationsHandler.markPublisherNews(owner, course.getResourceableId()); log.audit("Map " + map + " from " + owner.getName() + " has been submitted."); } } /** * Close all maps after the deadline if there is a deadline. It can be a long running * process if a lot of maps are involved. */ public void closeMapAfterDeadline() { List<PortfolioStructureMap> mapsToClose = structureManager.getOpenStructuredMapAfterDeadline(); int count = 0; for(PortfolioStructureMap mapToClose:mapsToClose) { submitMap(mapToClose, false); if(count % 5 == 0) { // this possibly takes longer than connection timeout, so do intermediatecommits. dbInstance.intermediateCommit(); } } } /** * get a valid name of style for a given PortfolioStructure * if style is not enabled anymore, the default will be used. * @param struct * @return the set style or the default from config if nothing is set. */ public String getValidStyleName(PortfolioStructure struct){ // first style in list is the default, can be named default. List<String> allStyles = portfolioModule.getAvailableMapStyles(); if (allStyles == null || allStyles.size() == 0) throw new AssertException("at least one style (that also exists in brasato.css must be configured for maps."); String styleName = ((EPStructureElement)struct).getStyle(); if(StringHelper.containsNonWhitespace(styleName) && allStyles.contains(styleName)) { return styleName; } return allStyles.get(0); } /** * The structure will be without any check on the DB copied. All the * children structures MUST be loaded. This method is to use with the * output of XStream at examples. * @param root * @param identity * @return The persisted structure */ public PortfolioStructureMap importPortfolioMapTemplate(PortfolioStructure root, OLATResource resource) { return structureManager.importPortfolioMapTemplate(root, resource); } /** * check if given identity has access to this feed. * reverse lookup feed -> artefact -> shared map * @param feed * @param identity * @return */ public boolean checkFeedAccess(OLATResourceable feed, Identity identity){ String feedBP = LiveBlogArtefactHandler.LIVEBLOG + feed.getResourceableId() + "]"; List<AbstractArtefact> artefact = loadArtefactsByBusinessPath(feedBP, null); if (artefact != null && artefact.size() == 1) { List<PortfolioStructure> linkedMaps = getReferencedMapsForArtefact(artefact.get(0)); for (PortfolioStructure map : linkedMaps) { if (isMapVisible(identity, map)){ return true; } } // see OLAT-6282: allow the owner of the artefact to view the feed, even if its not any longer in any map. if (linkedMaps.size() == 0 && artefact.get(0).getAuthor().equalsByPersistableKey(identity)){ return true; } } return false; } /** * returns all Owners of the given map as comma-separated list * @param map * @return */ public String getAllOwnersAsString(PortfolioStructureMap map){ if(map.getGroups() == null) { return null; } List<Identity> ownerIdents = policyManager.getOwners(map); List<String> identNames = new ArrayList<String>(); for (Identity identity : ownerIdents) { String fullName = userManager.getUserDisplayName(identity); if(fullName != null) { identNames.add(fullName); } } return StringHelper.formatAsCSVString(identNames); } /** * returns the first Owner for the given Map. * * @param map * @return */ public String getFirstOwnerAsString(PortfolioStructureMap map){ if(map.getGroups() == null) { return "n/a"; } List<Identity> ownerIdents = policyManager.getOwners(map); if(ownerIdents.size() > 0){ Identity id = ownerIdents.get(0); return userManager.getUserDisplayName(id); } return "n/a"; } public String getFirstOwnerAsString(EPMapShort map){ if(map.getGroups() == null) { return "n/a"; } List<Identity> ownerIdents = policyManager.getOwners(map); if(ownerIdents.size() > 0){ Identity id = ownerIdents.get(0); return userManager.getUserDisplayName(id); } return "n/a"; } /** * returns the first OwnerIdentity for the given Map. * * @param map * @return */ public Identity getFirstOwnerIdentity(PortfolioStructureMap map){ if(map.getGroups() == null) { return null; } List<Identity> ownerIdents = policyManager.getOwners(map); if (ownerIdents.size() > 0) { Identity id = ownerIdents.get(0); return id; } return null; } }