/** * <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> * BPS Bildungsportal Sachsen GmbH, http://www.bps-system.de * <p> */ package de.bps.course.nodes; import java.io.File; import java.io.IOException; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.io.IOUtils; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.stack.BreadcrumbPanel; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.tabbable.TabbableController; import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.ExportUtil; import org.olat.core.util.FileUtils; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.ValidationStatus; import org.olat.core.util.WebappHelper; import org.olat.core.util.xml.XStreamHelper; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.condition.Condition; import org.olat.course.condition.ConditionEditController; import org.olat.course.editor.CourseEditorEnv; import org.olat.course.editor.NodeEditController; import org.olat.course.editor.StatusDescription; import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.groupsandrights.CourseRights; import org.olat.course.nodes.AbstractAccessableCourseNode; import org.olat.course.nodes.ArchiveOptions; import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.StatusDescriptionHelper; import org.olat.course.nodes.TitledWrapperHelper; import org.olat.course.properties.CoursePropertyManager; import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.userview.NodeEvaluation; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.modules.ModuleConfiguration; import org.olat.properties.Property; import org.olat.repository.RepositoryEntry; import org.olat.util.logging.activity.LoggingResourceable; import com.thoughtworks.xstream.XStream; import de.bps.course.nodes.cl.ChecklistEditController; import de.bps.olat.modules.cl.Checklist; import de.bps.olat.modules.cl.ChecklistDisplayController; import de.bps.olat.modules.cl.ChecklistManager; import de.bps.olat.modules.cl.Checkpoint; import de.bps.olat.modules.cl.CheckpointMode; /** * Description:<br> * Checklist Course Node * * <P> * Initial Date: 23.07.2009 <br> * @author bja <bja@bps-system.de> * @author skoeber <skoeber@bps-system.de> */ public class ChecklistCourseNode extends AbstractAccessableCourseNode { private static final long serialVersionUID = -8978938639489414749L; private static final OLog log = Tracing.createLoggerFor(ChecklistCourseNode.class); private static final String TYPE = "cl"; public static final String CONF_COURSE_ID = "cl_course_id"; public static final String CONF_COURSE_NODE_ID = "cl_course_node_id"; public static final String CONF_CHECKLIST = "cl_checklist"; public static final String CONF_CHECKLIST_COPY = "cl_checklist_copy"; public static final String PROPERTY_CHECKLIST_KEY = CONF_CHECKLIST; public ChecklistCourseNode() { super(TYPE); initDefaultConfig(); } private void initDefaultConfig() { ModuleConfiguration config = getModuleConfiguration(); // add an empty checkpoint entry as default if none existent if (config.get(CONF_CHECKLIST) == null) { Checklist initialChecklist = new Checklist(); // set to config config.set(CONF_CHECKLIST, initialChecklist); // save to db ChecklistManager.getInstance().saveChecklist(initialChecklist); } } /** * Internal helper: save node configuration * @param cpm * @param checklistKey */ private void setChecklistKey(final CoursePropertyManager cpm, Long checklistKey) { Property checklistKeyProperty = cpm.createCourseNodePropertyInstance(this, null, null, PROPERTY_CHECKLIST_KEY, null, checklistKey, null, null); cpm.saveProperty(checklistKeyProperty); /* * Save reference to checklist additionally in module configuration since the CoursePropertyManager is not always available. */ getModuleConfiguration().set(CONF_CHECKLIST, ChecklistManager.getInstance().loadChecklist(checklistKey)); } /** * Internal helper: load node configuration * @param cpm * @return checklistKey */ private Long getChecklistKey(final CoursePropertyManager cpm) { Property checklistKeyProperty = cpm.findCourseNodeProperty(this, null, null, PROPERTY_CHECKLIST_KEY); return checklistKeyProperty != null ? checklistKeyProperty.getLongValue() : null; } /** * Internal helper: delete node configuration * @param cpm */ private void deleteChecklistKeyConf(final CoursePropertyManager cpm) { cpm.deleteNodeProperties(this, PROPERTY_CHECKLIST_KEY); getModuleConfiguration().remove(CONF_CHECKLIST); } /** * Load referenced checklist or create a new one * @param cpm * @return Checklist */ public Checklist loadOrCreateChecklist(final CoursePropertyManager cpm) { Checklist checklist; if(getChecklistKey(cpm) == null) { // this is a copied node, the checklist is referenced by createInstanceForCopy() if(getModuleConfiguration().get(CONF_CHECKLIST_COPY) != null) { checklist = ChecklistManager.getInstance().loadChecklist((Checklist) getModuleConfiguration().get(ChecklistCourseNode.CONF_CHECKLIST_COPY)); getModuleConfiguration().remove(CONF_CHECKLIST_COPY); } else // this is part of a copied course, the original checklist will be copied if(getModuleConfiguration().get(CONF_CHECKLIST) != null) { Checklist confChecklist = (Checklist)getModuleConfiguration().get(ChecklistCourseNode.CONF_CHECKLIST); Checklist orgChecklist = ChecklistManager.getInstance().loadChecklist(confChecklist); checklist = ChecklistManager.getInstance().copyChecklist(orgChecklist); } else { // no checklist available, create new one checklist = new Checklist(); ChecklistManager.getInstance().saveChecklist(checklist); } // set referenced checklist in configuration setChecklistKey(cpm, checklist.getKey()); } else { checklist = ChecklistManager.getInstance().loadChecklist(getChecklistKey(cpm)); } return checklist; } @Override public TabbableController createEditController(UserRequest ureq, WindowControl wControl, BreadcrumbPanel stackPanel, ICourse course, UserCourseEnvironment euce) { CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(euce.getCourseEditorEnv().getCurrentCourseNodeId()); ChecklistEditController editController = new ChecklistEditController(ureq, wControl, this, course, euce); getModuleConfiguration().set(CONF_COURSE_ID, course.getResourceableId()); getModuleConfiguration().set(CONF_COURSE_NODE_ID, chosenNode.getIdent()); NodeEditController nodeEditController = new NodeEditController(ureq, wControl, course.getEditorTreeModel(), course, chosenNode, euce, editController); nodeEditController.addControllerListener(editController); return nodeEditController; } @Override public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { ICourse course = CourseFactory.loadCourse(userCourseEnv.getCourseEnvironment().getCourseResourceableId()); CourseGroupManager cgm = course.getCourseEnvironment().getCourseGroupManager(); boolean canEdit = ureq.getUserSession().getRoles().isOLATAdmin() || cgm.isIdentityCourseAdministrator(ureq.getIdentity()); boolean canManage; if(canEdit) { canManage = true; } else { canManage = cgm.isIdentityCourseCoach(ureq.getIdentity()) || cgm.hasRight(ureq.getIdentity(), CourseRights.RIGHT_GROUPMANAGEMENT); } Checklist checklist = loadOrCreateChecklist(userCourseEnv.getCourseEnvironment().getCoursePropertyManager()); ChecklistDisplayController checkController = new ChecklistDisplayController(ureq, wControl, checklist, canEdit, canManage, userCourseEnv.isCourseReadOnly(), course); checkController.addLoggingResourceable(LoggingResourceable.wrap(this)); // Add title and descrition Controller controller = TitledWrapperHelper.getWrapper(ureq, wControl, checkController, this, "o_cl_icon"); return new NodeRunConstructionResult(controller); } @Override public StatusDescription[] isConfigValid(CourseEditorEnv cev) { oneClickStatusCache = null; String translatorStr = Util.getPackageName(ConditionEditController.class); List<StatusDescription> statusDescs = isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions()); oneClickStatusCache = StatusDescriptionHelper.sort(statusDescs); return oneClickStatusCache; } public RepositoryEntry getReferencedRepositoryEntry() { return null; } public StatusDescription isConfigValid() { if (oneClickStatusCache != null) { return oneClickStatusCache[0]; } StatusDescription sd = StatusDescription.NOERROR; String transPackage = ChecklistEditController.class.getPackage().getName(); // no configuration available hence there is no checklist with checkpoints if(getModuleConfiguration().get(ChecklistCourseNode.CONF_CHECKLIST) == null) { sd = new StatusDescription(ValidationStatus.ERROR, "config.nocheckpoints.short", "config.nocheckpoints.long", null, transPackage); sd.setDescriptionForUnit(getIdent()); sd.setActivateableViewIdentifier(ChecklistEditController.PANE_TAB_CLCONFIG); return sd; } Checklist checklist = (Checklist) getModuleConfiguration().get(ChecklistCourseNode.CONF_CHECKLIST); // checklist without any checkpoints makes no sense if (!checklist.hasCheckpoints()) { sd = new StatusDescription(ValidationStatus.ERROR, "config.nocheckpoints.short", "config.nocheckpoints.long", null, transPackage); sd.setDescriptionForUnit(getIdent()); sd.setActivateableViewIdentifier(ChecklistEditController.PANE_TAB_CLCONFIG); return sd; } // information, if all checkpoints are invisible boolean allUnvisible = true; boolean noLearners = false; if (checklist.hasCheckpoints()) { List<Checkpoint> checkpoints = ((Checklist)getModuleConfiguration().get(ChecklistCourseNode.CONF_CHECKLIST)).getCheckpoints(); for (Checkpoint checkpoint : checkpoints) { if (!checkpoint.getMode().equals(CheckpointMode.MODE_HIDDEN)) allUnvisible = false; } if(allUnvisible) { Condition cond = getPreConditionVisibility(); if(cond.isEasyModeCoachesAndAdmins()) noLearners = true; if(!noLearners) { sd = new StatusDescription(ValidationStatus.WARNING, "config.allhidden.short", "config.allhidden.long", null, transPackage); sd.setDescriptionForUnit(getIdent()); sd.setActivateableViewIdentifier(ChecklistEditController.PANE_TAB_CLCONFIG); } } } return sd; } public boolean needsReferenceToARepositoryEntry() { return false; } @Override public void cleanupOnDelete(ICourse course) { super.cleanupOnDelete(course); // delete checklist in db Checklist checklist = loadOrCreateChecklist(course.getCourseEnvironment().getCoursePropertyManager()); ChecklistManager.getInstance().deleteChecklist(checklist); checklist = null; // delete node configuration deleteChecklistKeyConf(course.getCourseEnvironment().getCoursePropertyManager()); } @Override public void exportNode(File exportDirectory, ICourse course) { XStream xstream = XStreamHelper.createXStreamInstance(); ChecklistManager cm = ChecklistManager.getInstance(); Checklist checklist = loadOrCreateChecklist(course.getCourseEnvironment().getCoursePropertyManager()); Checklist copy = cm.copyChecklistInRAM(checklist); String exportContent = xstream.toXML(copy); ExportUtil.writeContentToFile(getExportFilename(), exportContent, exportDirectory, WebappHelper.getDefaultCharset()); } @Override public void importNode(File importDirectory, ICourse course, Identity owner, Locale locale, boolean withReferences) { CoursePropertyManager cpm = course.getCourseEnvironment().getCoursePropertyManager(); if(getChecklistKey(cpm) != null) deleteChecklistKeyConf(cpm); File importFile = new File(importDirectory, getExportFilename()); String importContent = FileUtils.load(importFile, WebappHelper.getDefaultCharset()); if(importContent == null || importContent.isEmpty()) { return; } XStream xstream = XStreamHelper.createXStreamInstance(); Checklist checklist = (Checklist) xstream.fromXML(importContent); if(checklist != null) { checklist = ChecklistManager.getInstance().copyChecklist(checklist); setChecklistKey(cpm, checklist.getKey()); } } @Override public boolean archiveNodeData(Locale locale, ICourse course, ArchiveOptions options, ZipOutputStream exportStream, String charset) { String filename = "checklist_" + StringHelper.transformDisplayNameToFileSystemName(getShortName()) + "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())); Checklist checklist = loadOrCreateChecklist(course.getCourseEnvironment().getCoursePropertyManager()); String exportContent = XStreamHelper.createXStreamInstance().toXML(checklist); try { exportStream.putNextEntry(new ZipEntry(filename)); IOUtils.write(exportContent, exportStream); exportStream.closeEntry(); } catch (IOException e) { log.error("", e); } return true; } private String getExportFilename() { return "checklist_"+this.getIdent()+".xml"; } @Override public CourseNode createInstanceForCopy(boolean isNewTitle, ICourse course, Identity author) { CourseNode copyInstance = super.createInstanceForCopy(isNewTitle, course, author); ChecklistManager cm = ChecklistManager.getInstance(); // load checklist Checklist checklist = cm.loadChecklist((Checklist) getModuleConfiguration().get(ChecklistCourseNode.CONF_CHECKLIST)); // remove old config copyInstance.getModuleConfiguration().remove(ChecklistCourseNode.CONF_CHECKLIST); // create new checklist with same settings and save to db Checklist initialChecklist = cm.copyChecklist(checklist); // set to config copyInstance.getModuleConfiguration().set(CONF_CHECKLIST_COPY, initialChecklist); return copyInstance; } }