/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <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>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/
package org.olat.repository.controllers;
import org.olat.basesecurity.Constants;
import org.olat.basesecurity.Manager;
import org.olat.basesecurity.ManagerFactory;
import org.olat.basesecurity.SecurityGroup;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.DefaultController;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.translator.Translator;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.DBRuntimeException;
import org.olat.core.logging.Tracing;
import org.olat.core.util.Util;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryManager;
import org.olat.repository.handlers.BlogHandler;
import org.olat.repository.handlers.CourseHandler;
import org.olat.repository.handlers.GlossaryHandler;
import org.olat.repository.handlers.ImsCPHandler;
import org.olat.repository.handlers.PodcastHandler;
import org.olat.repository.handlers.QTIHandler;
import org.olat.repository.handlers.QTISurveyHandler;
import org.olat.repository.handlers.QTITestHandler;
import org.olat.repository.handlers.RepositoryHandler;
import org.olat.repository.handlers.SCORMCPHandler;
import org.olat.repository.handlers.SharedFolderHandler;
import org.olat.repository.handlers.WebDocumentHandler;
import org.olat.repository.handlers.WikiHandler;
import org.olat.resource.OLATResource;
import org.olat.resource.OLATResourceManager;
import org.olat.resource.references.ReferenceManager;
/**
* Description:<br>
*
* @author Felix Jost
*/
public class RepositoryAddController extends DefaultController {
private static final String VELOCITY_ROOT = Util.getPackageVelocityRoot(RepositoryManager.class);
static final String ACTION_ADD_PREFIX = "a.";
static final String ACTION_ADD_COURSE = ACTION_ADD_PREFIX + "co";
static final String ACTION_ADD_CP = ACTION_ADD_PREFIX + "cp";
static final String ACTION_ADD_SCORM = ACTION_ADD_PREFIX + "scorm";
static final String ACTION_ADD_TEST = ACTION_ADD_PREFIX + "te";
static final String ACTION_ADD_SURVEY = ACTION_ADD_PREFIX + "sv";
static final String ACTION_ADD_WIKI = ACTION_ADD_PREFIX + "wiki";
static final String ACTION_ADD_PODCAST = ACTION_ADD_PREFIX + "podcast";
static final String ACTION_ADD_BLOG = ACTION_ADD_PREFIX + "blog";
static final String ACTION_ADD_GLOSSARY = ACTION_ADD_PREFIX + "glossary";
static final String ACTION_ADD_DOC = ACTION_ADD_PREFIX + "dc";
static final String ACTION_NEW_COURSE = ACTION_ADD_PREFIX + "nco";
static final String ACTION_NEW_CP = ACTION_ADD_PREFIX + "ncp";
static final String ACTION_NEW_TEST = ACTION_ADD_PREFIX + "nte";
static final String ACTION_NEW_SURVEY = ACTION_ADD_PREFIX + "nsu";
static final String ACTION_NEW_SHAREDFOLDER = ACTION_ADD_PREFIX + "nsf";
static final String ACTION_NEW_WIKI = ACTION_ADD_PREFIX + "nwiki";
static final String ACTION_NEW_PODCAST = ACTION_ADD_PREFIX + "npodcast";
static final String ACTION_NEW_BLOG = ACTION_ADD_PREFIX + "nblog";
static final String ACTION_NEW_GLOSSARY = ACTION_ADD_PREFIX + "nglossary";
static final String ACTION_CANCEL = "cancel";
static final String ACTION_FORWARD = "forward";
private VelocityContainer repositoryadd;
private Translator translator;
private RepositoryEditDescriptionController detailsController;
private IAddController addController;
private RepositoryHandler typeToAdd;
private RepositoryAddCallback addCallback;
private Manager securityManager;
private RepositoryEntry addedEntry;
// flag is true when workflow has been finished successfully,
// otherwhise when disposing the controller or in a case of
// user abort / cancel the system will delete temporary data
private boolean workflowSuccessful = false;
private Link cancelButton;
private Link forwardButton;
/**
* Controller implementing "Add Repository Entry"-workflow.
* @param ureq
* @param wControl
* @param actionAddCommand
*/
public RepositoryAddController(UserRequest ureq, WindowControl wControl, String actionAddCommand) {
super(wControl);
translator = Util.createPackageTranslator(RepositoryManager.class, ureq.getLocale());
securityManager = ManagerFactory.getManager();
/*
* FIXME:pb: review: during constructor call -> /addDelegate.html is active first, then typeToAdd.getAddController() with
* this as addCallback may/should/must? call protected finished(..); which in turn replaces /addDelegate.html by /addDetails.html....
* what are the concepts here?
*/
repositoryadd = new VelocityContainer("repositoryadd", VELOCITY_ROOT + "/addDelegate.html", translator, this);
cancelButton = LinkFactory.createButton("cmd.cancel", repositoryadd, this);
forwardButton = LinkFactory.createButton("cmd.forward", repositoryadd, this);
String translatedTypeName = null;
String typeIntro = null;
addCallback = new RepositoryAddCallback(this);
if (actionAddCommand.equals(ACTION_ADD_COURSE)) {
typeToAdd = new CourseHandler();
addController = typeToAdd.getAddController(addCallback, CourseHandler.PROCESS_IMPORT, ureq, getWindowControl());
translatedTypeName = translator.translate("add.course");
typeIntro = translator.translate("add.course.intro");
}
else if (actionAddCommand.equals(ACTION_ADD_DOC)) {
typeToAdd = new WebDocumentHandler();
addController = typeToAdd.getAddController(addCallback, null, ureq, getWindowControl());
translatedTypeName = translator.translate("add.webdoc");
typeIntro = translator.translate("add.webdoc.intro");
}
else if (actionAddCommand.equals(ACTION_ADD_TEST)) {
typeToAdd = new QTITestHandler();
addController = typeToAdd.getAddController(addCallback, QTIHandler.PROCESS_UPLOAD, ureq, getWindowControl());
translatedTypeName = translator.translate("add.test");
typeIntro = translator.translate("add.test.intro");
}
else if (actionAddCommand.equals(ACTION_ADD_SURVEY)) {
typeToAdd = new QTISurveyHandler();
addController = typeToAdd.getAddController(addCallback, QTIHandler.PROCESS_UPLOAD, ureq, getWindowControl());
translatedTypeName = translator.translate("add.survey");
typeIntro = translator.translate("add.survey.intro");
}
else if (actionAddCommand.equals(ACTION_ADD_CP)) {
typeToAdd = new ImsCPHandler();
addController = typeToAdd.getAddController(addCallback, ImsCPHandler.PROCESS_IMPORT, ureq, getWindowControl());
translatedTypeName = translator.translate("add.cp");
typeIntro = translator.translate("add.cp.intro");
}
else if (actionAddCommand.equals(ACTION_ADD_SCORM)) {
typeToAdd = new SCORMCPHandler();
addController = typeToAdd.getAddController(addCallback, null, ureq, getWindowControl());
translatedTypeName = translator.translate("add.scorm");
typeIntro = translator.translate("add.scorm.intro");
}
else if (actionAddCommand.equals(ACTION_ADD_WIKI)) {
typeToAdd = new WikiHandler();
addController = typeToAdd.getAddController(addCallback, null, ureq, getWindowControl());
translatedTypeName = translator.translate("add.wiki");
typeIntro = translator.translate("add.wiki.intro");
}
else if (actionAddCommand.equals(ACTION_ADD_PODCAST)) {
typeToAdd = new PodcastHandler();
addController = typeToAdd.getAddController(addCallback, null, ureq, getWindowControl());
translatedTypeName = translator.translate("add.podcast");
typeIntro = translator.translate("add.podcast.intro");
}
else if (actionAddCommand.equals(ACTION_ADD_BLOG)) {
typeToAdd = new BlogHandler();
addController = typeToAdd.getAddController(addCallback, null, ureq, getWindowControl());
translatedTypeName = translator.translate("add.blog");
typeIntro = translator.translate("add.blog.intro");
}
else if (actionAddCommand.equals(ACTION_ADD_GLOSSARY)) {
typeToAdd = new GlossaryHandler();
addController = typeToAdd.getAddController(addCallback, null, ureq, getWindowControl());
translatedTypeName = translator.translate("add.glossary");
typeIntro = translator.translate("add.glossary.intro");
}
else if (actionAddCommand.equals(ACTION_NEW_COURSE)) {
typeToAdd = new CourseHandler();
addController = typeToAdd.getAddController(addCallback, CourseHandler.PROCESS_CREATENEW, ureq, getWindowControl());
translatedTypeName = translator.translate("new.course");
typeIntro = translator.translate("new.course.intro");
}
else if (actionAddCommand.equals(ACTION_NEW_TEST)) {
typeToAdd = new QTITestHandler();
addController = typeToAdd.getAddController(addCallback, QTIHandler.PROCESS_CREATENEW, ureq, getWindowControl());
translatedTypeName = translator.translate("new.test");
typeIntro = translator.translate("new.test.intro");
}
else if (actionAddCommand.equals(ACTION_NEW_SURVEY)) {
typeToAdd = new QTISurveyHandler();
addController = typeToAdd.getAddController(addCallback, QTIHandler.PROCESS_CREATENEW, ureq, getWindowControl());
translatedTypeName = translator.translate("new.survey");
typeIntro = translator.translate("new.survey.intro");
}
else if (actionAddCommand.equals(ACTION_NEW_SHAREDFOLDER)) {
typeToAdd = new SharedFolderHandler();
addController = typeToAdd.getAddController(addCallback, SharedFolderHandler.PROCESS_CREATENEW, ureq, getWindowControl());
translatedTypeName = translator.translate("new.sharedfolder");
typeIntro = translator.translate("new.sharedfolder.intro");
}
else if (actionAddCommand.equals(ACTION_NEW_WIKI)) {
typeToAdd = new WikiHandler();
addController = typeToAdd.getAddController(addCallback, WikiHandler.PROCESS_CREATENEW, ureq, getWindowControl());
translatedTypeName = translator.translate("new.wiki");
typeIntro = translator.translate("new.wiki.intro");
}
else if (actionAddCommand.equals(ACTION_NEW_PODCAST)) {
typeToAdd = new PodcastHandler();
addController = typeToAdd.getAddController(addCallback, PodcastHandler.PROCESS_CREATENEW, ureq, getWindowControl());
translatedTypeName = translator.translate("new.podcast");
typeIntro = translator.translate("new.podcast.intro");
}
else if (actionAddCommand.equals(ACTION_NEW_BLOG)) {
typeToAdd = new BlogHandler();
addController = typeToAdd.getAddController(addCallback, BlogHandler.PROCESS_CREATENEW, ureq, getWindowControl());
translatedTypeName = translator.translate("new.blog");
typeIntro = translator.translate("new.blog.intro");
}
else if (actionAddCommand.equals(ACTION_NEW_GLOSSARY)) {
typeToAdd = new GlossaryHandler();
addController = typeToAdd.getAddController(addCallback, GlossaryHandler.PROCESS_CREATENEW, ureq, getWindowControl());
translatedTypeName = translator.translate("new.glossary");
typeIntro = translator.translate("new.glossary.intro");
}
else if (actionAddCommand.equals(ACTION_NEW_CP)) {
typeToAdd = new ImsCPHandler();
addController = typeToAdd.getAddController(addCallback, ImsCPHandler.PROCESS_CREATENEW, ureq, getWindowControl());
translatedTypeName = translator.translate("tools.add.cp");
typeIntro = translator.translate("new.cp.intro");
}
else throw new AssertException("Unsuported Repository Type.");
// AddControllers may not need a GUI-based workflow.
// In such cases, they do not have to return a transactional component,
// but they must call addCallback.finished().
Component transactionComponent = addController.getTransactionComponent();
if (transactionComponent != null) repositoryadd.put("subcomp", transactionComponent);
repositoryadd.contextPut("typeHeader", (translatedTypeName == null) ?
translator.translate("add.header") :
translator.translate("add.header.specific", new String[] {translatedTypeName}));
repositoryadd.contextPut("typeIntro", typeIntro);
forwardButton.setEnabled(false);
forwardButton.setTextReasonForDisabling(translator.translate("disabledforwardreason"));
setInitialComponent(repositoryadd);
return;
}
/**
* Used by RepositoryMainController to identify which resource has been added.
*/
RepositoryEntry getAddedEntry() { return addedEntry; }
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.components.Component, org.olat.core.gui.control.Event)
*/
public void event(UserRequest ureq, Component source, Event event) {
if (source == forwardButton){
// finish transaction and add repository entry
if (!addController.transactionFinishBeforeCreate()) return;
// Do set access for owner at the end, because unfinished course should be invisible
addedEntry = (RepositoryEntry) DBFactory.getInstance().loadObject(addedEntry); // need a reload from hibernate because create a new cp load a repository-entry (OLAT-5631)
addedEntry.setAccess(RepositoryEntry.ACC_OWNERS);
RepositoryManager.getInstance().updateRepositoryEntry(addedEntry);
addController.repositoryEntryCreated(addedEntry);
workflowSuccessful = true;
fireEvent(ureq, Event.DONE_EVENT);
fireEvent(ureq, new EntryChangedEvent(addedEntry, EntryChangedEvent.ADDED));
return;
} else if (source == cancelButton){
// FIXME:pb: review is it really as intended to pass here from /addDelegate.html or /addDetails.html
// clean up temporary data and abort transaction
cleanup();
fireEvent(ureq, Event.CANCELLED_EVENT);
return;
}
}
public void event(UserRequest ureq, Controller source, Event event) {
if (source == detailsController) {
if (event == Event.CANCELLED_EVENT) {
// clean upd temporary data and abort transaction
cleanup();
fireEvent(ureq, Event.CANCELLED_EVENT);
return;
} else if (event == Event.DONE_EVENT) {
forwardButton.setEnabled(true);
addedEntry = detailsController.getRepositoryEntry();
}
}
}
protected void addFinished(UserRequest ureq) {
addedEntry = RepositoryManager.getInstance()
.createRepositoryEntryInstance(ureq.getIdentity().getName());
addedEntry.setCanDownload(false);
addedEntry.setCanLaunch(typeToAdd.supportsLaunch());
String dispName = addCallback.getDisplayName();
if (dispName == null) dispName = "";
addedEntry.setDisplayname(dispName);
String resName = addCallback.getResourceName();
if (resName == null) resName = "";
addedEntry.setResourcename(resName);
// Do set access for owner at the end, because unfinished course should be invisible
// addedEntry.setAccess(RepositoryEntry.ACC_OWNERS);
addedEntry.setAccess(0);//Access for nobody
// Set the resource on the repository entry and save the entry.
RepositoryManager rm = RepositoryManager.getInstance();
OLATResource ores = OLATResourceManager.getInstance().findOrPersistResourceable(addCallback.getResourceable());
addedEntry.setOlatResource(ores);
// create security group
SecurityGroup newGroup = securityManager.createAndPersistSecurityGroup();
// member of this group may modify member's membership
securityManager.createAndPersistPolicy(newGroup, Constants.PERMISSION_ACCESS, newGroup);
// members of this group are always authors also
securityManager.createAndPersistPolicy(newGroup, Constants.PERMISSION_HASROLE, Constants.ORESOURCE_AUTHOR);
securityManager.addIdentityToSecurityGroup(ureq.getIdentity(), newGroup);
addedEntry.setOwnerGroup(newGroup);
rm.saveRepositoryEntry(addedEntry);
detailsController = new RepositoryEditDescriptionController(ureq, getWindowControl(), addedEntry, true);
detailsController.addControllerListener(this);
repositoryadd.put("details", detailsController.getInitialComponent());
// try to get type description based on handlertype
repositoryadd.contextPut("header",
translator.translate("add.header.specific", new String[] {translator.translate(ores.getResourceableTypeName())}));
repositoryadd.setPage(VELOCITY_ROOT + "/addDetails.html");
}
protected void addCanceled(UserRequest ureq) {
cleanup();
fireEvent(ureq, Event.CANCELLED_EVENT);
}
protected void addFailed(UserRequest ureq) {
cleanup();
fireEvent(ureq, Event.FAILED_EVENT);
}
private void cleanup() {
Tracing.logDebug("cleanup : start addedEntry=" + addedEntry, RepositoryAddController.class);
if (detailsController != null) {
addedEntry = detailsController.getRepositoryEntry();
}
if (addedEntry != null) {
// delete the repository entry and its olatresource
try {
//o_clusterREVIEW
Tracing.logDebug("cleanup : addedEntry=" + addedEntry, RepositoryAddController.class);
addedEntry = (RepositoryEntry) DBFactory.getInstance().loadObject(addedEntry,true);
SecurityGroup secGroup = addedEntry.getOwnerGroup();
Tracing.logDebug("cleanup : deleteAllReferencesOf", RepositoryAddController.class);
ReferenceManager.getInstance().deleteAllReferencesOf(addedEntry.getOlatResource());
Tracing.logDebug("cleanup : deleteRepositoryEntry", RepositoryAddController.class);
RepositoryManager.getInstance().deleteRepositoryEntry(addedEntry);
Tracing.logDebug("cleanup : deleteOLATResource", RepositoryAddController.class);
OLATResourceManager.getInstance().deleteOLATResource(addedEntry.getOlatResource());
if (secGroup != null) { // delete owner group
Tracing.logDebug("cleanup : deleteSecurityGroup", RepositoryAddController.class);
securityManager.deleteSecurityGroup(secGroup);
}
if (detailsController != null) {
detailsController.dispose();
detailsController = null;
}
addedEntry = null;
Tracing.logDebug("cleanup : done", RepositoryAddController.class);
} catch (DBRuntimeException ex) {
Tracing.logError("Can not cleanup properly ", ex, RepositoryAddController.class);
}
}
// tell add controller about this
addController.transactionAborted();
Tracing.logDebug("cleanup : finished", RepositoryAddController.class);
}
/**
*
* @see org.olat.core.gui.control.DefaultController#doDispose(boolean)
*/
protected void doDispose() {
if (!workflowSuccessful) {
cleanup();
}
if (detailsController != null) {
detailsController.dispose();
detailsController = null;
}
}
}