/**
* <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.webFeed.ui;
import java.util.List;
import org.olat.core.commons.services.notifications.PublisherData;
import org.olat.core.commons.services.notifications.SubscriptionContext;
import org.olat.core.commons.services.notifications.ui.ContextualSubscriptionController;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.form.flexible.elements.FileElement;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
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.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.controller.BasicController;
import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
import org.olat.core.gui.control.generic.dtabs.Activateable2;
import org.olat.core.id.OLATResourceable;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.id.context.StateEntry;
import org.olat.core.logging.activity.ThreadLocalUserActivityLogger;
import org.olat.core.util.coordinate.CoordinatorManager;
import org.olat.core.util.coordinate.LockResult;
import org.olat.core.util.event.GenericEventListener;
import org.olat.core.util.resource.OLATResourceableJustBeforeDeletedEvent;
import org.olat.modules.webFeed.FeedSecurityCallback;
import org.olat.modules.webFeed.FeedViewHelper;
import org.olat.modules.webFeed.managers.FeedManager;
import org.olat.modules.webFeed.models.Feed;
import org.olat.modules.webFeed.models.Item;
import org.olat.user.UserManager;
import org.olat.util.logging.activity.LoggingResourceable;
import org.springframework.beans.factory.annotation.Autowired;
/**
* This is the main feed layout controller. It handles everything from adding
* episodes to changing title and description.
*
* <P>
* Initial Date: Feb 5, 2009 <br>
*
* @author gwassmann
*/
public class FeedMainController extends BasicController implements Activateable2, GenericEventListener {
private Feed feed;
private Link editFeedButton;
private CloseableModalController cmc;
private FeedFormController feedFormCtr;
private VelocityContainer vcMain, vcInfo, vcRightCol;
private ItemsController itemsCtr;
private LockResult lock;
private FeedViewHelper helper;
private DisplayFeedUrlController displayUrlCtr;
private FeedUIFactory uiFactory;
private FeedSecurityCallback callback;
private ContextualSubscriptionController cSubscriptionCtrl;
// needed for comparison
private String oldFeedUrl;
private SubscriptionContext subsContext;
private OLATResourceable ores;
@Autowired
private UserManager userManager;
@Autowired
private FeedManager feedManager;
/**
* Constructor for learning resource (not course nodes)
*
* @param ores
* @param ureq
* @param wControl
* @param previewMode Indicates that the content will only be displayed in
* preview and no editing functionality is enabled.
*/
public FeedMainController(OLATResourceable ores, UserRequest ureq, WindowControl wControl, FeedUIFactory uiFactory,
FeedSecurityCallback callback) {
this(ores, ureq, wControl, null, null, uiFactory, callback, null);
}
/**
* Constructor for course node
*
* @param ores
* @param ureq
* @param wControl
* @param previewMode Indicates that the content will only be displayed in
* preview and no editing functionality is enabled.
*/
public FeedMainController(OLATResourceable ores, UserRequest ureq, WindowControl wControl, Long courseId, String nodeId,
FeedUIFactory uiFactory, FeedSecurityCallback callback, FeedItemDisplayConfig displayConfig) {
super(ureq, wControl);
this.uiFactory = uiFactory;
this.callback = callback;
this.ores = ores;
subsContext = callback.getSubscriptionContext();
setTranslator(uiFactory.getTranslator());
feed = feedManager.getFeed(ores);
if(feed == null) {
vcMain = createVelocityContainer("feed_error");
vcMain.contextPut("errorMessage", translate("feed.error"));
putInitialPanel(vcMain);
} else {
String authorFullname = userManager.getUserDisplayName(feed.getAuthor());
if(authorFullname == null) {
authorFullname = "???";
}
helper = new FeedViewHelper(feed, getIdentity(), authorFullname, uiFactory.getTranslator(), courseId, nodeId, callback);
CoordinatorManager.getInstance().getCoordinator().getEventBus().registerFor(this, ureq.getIdentity(), feed);
display(ureq, wControl, displayConfig);
// do logging
ThreadLocalUserActivityLogger.log(FeedLoggingAction.FEED_READ, getClass(), LoggingResourceable.wrap(feed));
}
}
@Override
protected void doDispose() {
feedManager.releaseLock(lock);
if(feed != null) {
CoordinatorManager.getInstance().getCoordinator().getEventBus().deregisterFor(this, feed);
}
}
/**
* Sets up the velocity container for displaying the view
*
* @param ores
* @param ureq
* @param wControl
* @param previewMode
* @param isCourseNode
*/
private void display(UserRequest ureq, WindowControl wControl, FeedItemDisplayConfig displayConfig) {
vcMain = createVelocityContainer("feed_main");
vcInfo = uiFactory.createInfoVelocityContainer(this);
vcInfo.contextPut("feed", feed);
vcInfo.contextPut("helper", helper);
if (subsContext != null) {
String businnessPath = wControl.getBusinessControl().getAsString();
PublisherData data = new PublisherData(ores.getResourceableTypeName(), ores.getResourceableId().toString(), businnessPath);
cSubscriptionCtrl = new ContextualSubscriptionController(ureq, getWindowControl(), subsContext, data);
listenTo(cSubscriptionCtrl);
vcInfo.put("subscription", cSubscriptionCtrl.getInitialComponent());
}
vcRightCol = uiFactory.createRightColumnVelocityContainer(this);
vcMain.put("rightColumn", vcRightCol);
// The current user has edit rights if he/she is an administrator or an
// owner of the resource.
if (callback.mayEditMetadata()) {
editFeedButton = LinkFactory.createButtonSmall("feed.edit", vcInfo, this);
editFeedButton.setElementCssClass("o_sel_feed_edit");
}
vcInfo.contextPut("callback", callback);
displayUrlCtr = new DisplayFeedUrlController(ureq, wControl, feed, helper, uiFactory.getTranslator());
listenTo(displayUrlCtr);
vcInfo.put("feedUrlComponent", displayUrlCtr.getInitialComponent());
vcMain.put("info", vcInfo);
itemsCtr = new ItemsController(ureq, wControl, feed, helper, uiFactory, callback, vcRightCol, displayConfig);
listenTo(itemsCtr);
vcMain.put("items", itemsCtr.getInitialComponent());
putInitialPanel(vcMain);
}
/**
* @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)
*/
@Override
protected void event(UserRequest ureq, Component source, Event event) {
// feed for this event and make sure the updated feed object is in the view
feed = feedManager.getFeed(feed);
vcInfo.contextPut("feed", feed);
if (source == editFeedButton) {
lock = feedManager.acquireLock(feed, ureq.getIdentity());
if (lock.isSuccess()) {
if (feed.isExternal()) {
oldFeedUrl = feed.getExternalFeedUrl();
}
feedFormCtr = new FeedFormController(ureq, getWindowControl(), feed, uiFactory);
activateModalDialog(feedFormCtr, uiFactory.getTranslator().translate("feed.edit"));
} else {
String fullName = userManager.getUserDisplayName(lock.getOwner());
showInfo("feed.is.being.edited.by", fullName);
}
}
}
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest,
* org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event)
*/
protected void event(UserRequest ureq, Controller source, Event event) {
if (source == cmc) {
if (event.equals(CloseableModalController.CLOSE_MODAL_EVENT)) {
removeAsListenerAndDispose(cmc);
cmc = null;
removeAsListenerAndDispose(feedFormCtr);
feedFormCtr = null;
// If the user cancels the first time after deciding to subscribe to
// an external feed, undo his decision
if (feed.isExternal()) {
if (oldFeedUrl == null || "".equals(oldFeedUrl)) {
feed = feedManager.updateFeedMode(null, feed);
itemsCtr.makeInternalAndExternalButtons();
}
}
//release lock
feedManager.releaseLock(lock);
}
} else if (source == feedFormCtr) {
if (event.equals(Event.CHANGED_EVENT) || event.equals(Event.CANCELLED_EVENT)) {
// Dispose the cmc and the feedFormCtr.
cmc.deactivate();
removeAsListenerAndDispose(cmc);
cmc = null;
if (event.equals(Event.CHANGED_EVENT)) {
vcInfo.setDirty(true);
// For external podcasts, set the feed to undefined if the feed url
// has been set empty.
if (feed.isExternal()) {
String newFeed = feed.getExternalFeedUrl();
displayUrlCtr.setUrl(newFeed);
if (newFeed == null) {
feed.setExternal(null);
itemsCtr.makeInternalAndExternalButtons();
// No more episodes to display
itemsCtr.resetItems(ureq, feed);
} else if (!newFeed.equals(oldFeedUrl)) {
// Set the episodes dirty since the feed url changed.
itemsCtr.resetItems(ureq, feed);
}
// Set the URIs correctly
helper.setURIs();
}
//handle image-changes if any
if (feedFormCtr.isImageDeleted()) {
feedManager.deleteImage(feed);
} else {
// set the image
FileElement image = null;
image = feedFormCtr.getFile();
feedManager.setImage(image, feed);
}
// Eventually update the feed
feed = feedManager.updateFeedMetadata(feed);
// Dispose the feedFormCtr
removeAsListenerAndDispose(feedFormCtr);
feedFormCtr = null;
// do logging
ThreadLocalUserActivityLogger.log(FeedLoggingAction.FEED_EDIT, getClass(), LoggingResourceable.wrap(feed));
} else if (event.equals(Event.CANCELLED_EVENT)) {
// If the user cancels the first time after deciding to subscribe to
// an external feed, undo his decision
if (feed.isExternal()) {
if (oldFeedUrl == null || "".equals(oldFeedUrl)) {
feed = feedManager.updateFeedMode(null, feed);
itemsCtr.makeInternalAndExternalButtons();
}
}
}
// release the lock
feedManager.releaseLock(lock);
removeAsListenerAndDispose(feedFormCtr);
feedFormCtr = null;
}
} else if (source == itemsCtr && event.equals(ItemsController.HANDLE_NEW_EXTERNAL_FEED_DIALOG_EVENT)) {
oldFeedUrl = feed.getExternalFeedUrl();
feedFormCtr = new FeedFormController(ureq, getWindowControl(), feed, uiFactory);
activateModalDialog(feedFormCtr, uiFactory.getTranslator().translate("feed.edit"));
} else if (source == itemsCtr && event.equals(ItemsController.FEED_INFO_IS_DIRTY_EVENT)) {
vcInfo.setDirty(true);
}
}
/**
* @param controller The <code>FormBasicController</code> to be displayed in
* the modal dialog.
*/
private void activateModalDialog(FormBasicController controller, String title) {
listenTo(controller);
cmc = new CloseableModalController(getWindowControl(), translate("close"), controller.getInitialComponent(), true, title);
listenTo(cmc);
cmc.activate();
}
@Override
public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
if(entries == null || entries.isEmpty()) return;
String itemId = entries.get(0).getOLATResourceable().getResourceableTypeName();
if(itemId != null && itemId.startsWith("item=")) {
itemId = itemId.substring(5, itemId.length());
}
int index = feed.getItemIds().indexOf(itemId);
if (index >= 0) {
Item item = feed.getItems().get(index);
itemsCtr.activate(ureq, item);
}
}
/**
* @see org.olat.core.util.event.GenericEventListener#event(org.olat.core.gui.control.Event)
*/
public void event(Event event) {
if (event instanceof OLATResourceableJustBeforeDeletedEvent) {
OLATResourceableJustBeforeDeletedEvent ojde = (OLATResourceableJustBeforeDeletedEvent) event;
// make sure it is our course (actually not needed till now, since we
// registered only to one event, but good style.
if (ojde.targetEquals(feed, true)) {
dispose();
}
}
}
}