/** * <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.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringEscapeUtils; import org.olat.NewControllerFactory; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.controllers.navigation.Dated; import org.olat.core.commons.controllers.navigation.NavigationEvent; import org.olat.core.commons.controllers.navigation.YearNavigationController; import org.olat.core.commons.services.commentAndRating.CommentAndRatingDefaultSecurityCallback; import org.olat.core.commons.services.commentAndRating.CommentAndRatingSecurityCallback; import org.olat.core.commons.services.commentAndRating.CommentAndRatingService; import org.olat.core.commons.services.commentAndRating.ui.UserCommentsAndRatingsController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.date.DateComponentFactory; 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.panel.Panel; 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.gui.control.generic.modal.DialogBoxController; import org.olat.core.gui.control.generic.modal.DialogBoxUIFactory; import org.olat.core.id.Identity; import org.olat.core.id.context.BusinessControlFactory; 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.CodeHelper; import org.olat.core.util.coordinate.LockResult; import org.olat.core.util.vfs.VFSContainer; import org.olat.modules.portfolio.PortfolioV2Module; import org.olat.modules.portfolio.ui.component.MediaCollectorComponent; 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.modules.webFeed.models.ItemPublishDateComparator; import org.olat.modules.webFeed.portfolio.BlogEntryMedia; import org.olat.modules.webFeed.portfolio.BlogEntryMediaHandler; import org.olat.portfolio.EPUIFactory; import org.olat.user.UserManager; import org.olat.util.logging.activity.LoggingResourceable; import org.springframework.beans.factory.annotation.Autowired; /** * This class is responsible for dealing with items. For internal podcasts, * items can be created, edited and deleted. * * Initial Date: Mar 2, 2009 <br> * * @author gwassmann */ public class ItemsController extends BasicController implements Activateable2 { private VelocityContainer vcItems; private ArrayList<Link> editButtons; private ArrayList<Link> deleteButtons; private ArrayList<Link> itemLinks; private Map<Item,Controller> artefactLinks; private Map<Item,Controller> commentsLinks; private Link addItemButton, makeInternalButton, makeExternalButton, olderItemsLink, newerItemsLink, startpageLink; private FormBasicController itemFormCtr; private CloseableModalController cmc; private DialogBoxController confirmDialogCtr; private Feed feedResource; private Item currentItem; private FeedViewHelper helper; private FeedUIFactory uiFactory; private YearNavigationController naviCtr; private FeedSecurityCallback callback; private Panel mainPanel; private ItemController itemCtr; //private int allItemsCount = 0; private List<ItemId> allItemIds; // Only one lock variable is needed, since only one item can be edited // at a time. private LockResult lock; private FeedItemDisplayConfig displayConfig; public static Event HANDLE_NEW_EXTERNAL_FEED_DIALOG_EVENT = new Event("cmd.handle.new.external.feed.dialog"); public static Event FEED_INFO_IS_DIRTY_EVENT = new Event("cmd.feed.info.is.dirty"); @Autowired private UserManager userManager; @Autowired private PortfolioV2Module portfolioModule; @Autowired private BlogEntryMediaHandler blogMediaHandler; /** * default constructor, with full FeedItemDisplayConfig * @param ureq * @param wControl * @param feed * @param helper * @param uiFactory * @param callback * @param vcRightColumn */ public ItemsController(final UserRequest ureq, final WindowControl wControl, final Feed feed, final FeedViewHelper helper, final FeedUIFactory uiFactory, final FeedSecurityCallback callback, final VelocityContainer vcRightColumn) { this(ureq, wControl, feed, helper, uiFactory, callback, vcRightColumn, null); } /** * load items with a given displayconfig * @param ureq * @param wControl * @param feed * @param helper * @param uiFactory * @param callback * @param vcRightColumn * @param displayConfig */ public ItemsController(final UserRequest ureq, final WindowControl wControl, final Feed feed, final FeedViewHelper helper, final FeedUIFactory uiFactory, final FeedSecurityCallback callback, final VelocityContainer vcRightColumn, FeedItemDisplayConfig displayConfig) { super(ureq, wControl); if (displayConfig == null) { displayConfig = new FeedItemDisplayConfig(true, true, true); } this.displayConfig = displayConfig; this.feedResource = feed; this.helper = helper; this.uiFactory = uiFactory; this.callback = callback; setTranslator(uiFactory.getTranslator()); vcItems = uiFactory.createItemsVelocityContainer(this); vcItems.contextPut("feed", feed); vcItems.contextPut("callback", callback); vcItems.contextPut("helper", helper); olderItemsLink = LinkFactory.createLink("feed.older.items", vcItems, this); olderItemsLink.setCustomDisplayText("«"); olderItemsLink.setCustomEnabledLinkCSS("o_backward"); olderItemsLink.setTitle("feed.older.items"); newerItemsLink = LinkFactory.createLink("feed.newer.items", vcItems, this); newerItemsLink.setCustomEnabledLinkCSS("o_forward"); newerItemsLink.setCustomDisplayText("»"); newerItemsLink.setTitle("feed.newer.items"); startpageLink = LinkFactory.createLink("feed.startpage", vcItems, this); startpageLink.setCustomEnabledLinkCSS("o_first_page"); createEditButtons(ureq, feed); // Add item details page link createItemLinks(feed); // Add item user comments link and rating if (displayConfig.isShowCRInMinimized()) { createCommentsAndRatingsLinks(ureq, feed); } // Add date components createDateComponents(feed); // The year/month navigation List<Item> items = feed.getFilteredItems(callback, ureq.getIdentity()); setAllItemIds(items); naviCtr = new YearNavigationController(ureq, wControl, getTranslator(), items); listenTo(naviCtr); if (displayConfig.isShowDateNavigation()){ vcRightColumn.put("navi", naviCtr.getInitialComponent()); } mainPanel = new Panel("mainPanel"); mainPanel.setContent(vcItems); this.putInitialPanel(mainPanel); } private void setAllItemIds(List<Item> items) { allItemIds = new ArrayList<ItemId>(); for(Item item:items) { allItemIds.add(new ItemId(item)); } } private boolean isSameAllItems(List<Item> items) { if(allItemIds == null) return false; List<ItemId> itemIds = new ArrayList<ItemId>(); for(Item item:items) { itemIds.add(new ItemId(item)); } return allItemIds.containsAll(itemIds) && itemIds.containsAll(allItemIds); } /** * Creates all necessary buttons for editing the feed's items * @param feed the current feed object */ private void createEditButtons(UserRequest ureq, Feed feed) { List<Item> items = feed.getCopiedListOfItems(); editButtons = new ArrayList<>(); deleteButtons = new ArrayList<>(); artefactLinks = new HashMap<>(); if (feed.isInternal()) { addItemButton = LinkFactory.createButtonSmall("feed.add.item", vcItems, this); addItemButton.setElementCssClass("o_sel_feed_item_new"); if (items != null) { for (Item item : items) { createButtonsForItem(ureq, feed, item); } } } else if (feed.isUndefined()) { // The feed is whether internal nor external: // That is, // - it has just been created, // - all items have been removed or // - the feed url of an external feed has been set empty. // In such a case, the user can decide whether to make it internal or // external. makeInternalAndExternalButtons(); } } /** * Create the comments and rating components for each feed item * * @param ureq * @param feed */ private void createCommentsAndRatingsLinks(UserRequest ureq, Feed feed) { List<Item> items = feed.getCopiedListOfItems(); if (items != null) { for (Item item : items) { // Add rating and commenting controller createCommentsAndRatingsLink(ureq, feed, item); } } } /** * Create comments and rating component link for given feed item * @param ureq * @param feed * @param item */ private void createCommentsAndRatingsLink(UserRequest ureq, Feed feed, Item item) { if(feed == null || item == null) return;//check against concurrent changes if (CoreSpringFactory.containsBean(CommentAndRatingService.class)) { if(commentsLinks == null) { commentsLinks = new HashMap<Item,Controller>(); } else if(commentsLinks.containsKey(item)) { removeAsListenerAndDispose(commentsLinks.get(item)); } boolean anonym = ureq.getUserSession().getRoles().isGuestOnly(); CommentAndRatingSecurityCallback secCallback = new CommentAndRatingDefaultSecurityCallback(getIdentity(), callback.mayEditMetadata(), anonym); UserCommentsAndRatingsController commentsAndRatingCtr = new UserCommentsAndRatingsController(ureq, getWindowControl(), feed, item.getGuid(), secCallback, true, true, false); commentsAndRatingCtr.setUserObject(item); listenTo(commentsAndRatingCtr); commentsLinks.put(item, commentsAndRatingCtr); String guid = item.getGuid(); vcItems.put("commentsAndRating." + guid, commentsAndRatingCtr.getInitialComponent()); } } /** * Create a GUI component to display a nicely formatted date * * @param ureq * @param feed */ private void createDateComponents(Feed feed) { List<Item> items = feed.getCopiedListOfItems(); if (items != null) { for (Item item : items) { String guid = item.getGuid(); if(item.getDate() != null) { DateComponentFactory.createDateComponentWithYear("date." + guid, item.getDate(), vcItems); } } } } private void createItemLinks(Feed feed) { List<Item> items = feed.getCopiedListOfItems(); itemLinks = new ArrayList<Link>(); if (items != null) { for (Item item : items) { createItemLink(item); } } } /** * @param item */ private void createItemLink(Item item) { String guid = item.getGuid(); Link itemLink_more = LinkFactory.createCustomLink("link.to." + guid, "link.to." + guid, "feed.link.more", Link.LINK, vcItems, this); itemLink_more.setIconRightCSS("o_icon o_icon_start"); itemLink_more.setCustomEnabledLinkCSS("o_link_forward"); itemLink_more.setUserObject(item); Link itemLink_title = LinkFactory.createCustomLink("titlelink.to." + guid, "titlelink.to." + guid, StringEscapeUtils.escapeHtml(item.getTitle()), Link.NONTRANSLATED, vcItems, this); itemLink_title.setUserObject(item); itemLinks.add(itemLink_title); itemLinks.add(itemLink_more); } /** * Instantiates the makeInternal and the makeExternal-Buttons and puts it to * the items velocity container's context. */ public void makeInternalAndExternalButtons() { if (callback.mayEditItems() || callback.mayCreateItems()) { makeInternalButton = LinkFactory.createButton("feed.make.internal", vcItems, this); makeExternalButton = LinkFactory.createButton("feed.make.external", vcItems, this); } } /** * @param item */ private void createButtonsForItem(UserRequest ureq, Feed feed, Item item) { boolean author = getIdentity().getKey().equals(item.getAuthorKey()); boolean edit = callback.mayEditItems() || (author && callback.mayEditOwnItems()); boolean delete = callback.mayDeleteItems() || (author && callback.mayDeleteOwnItems()); String guid = item.getGuid(); String editId = "feed.edit.item.".concat(guid); Link editButton = LinkFactory.createCustomLink(editId, editId, "feed.edit.item", Link.BUTTON_SMALL, vcItems, this); editButton.setElementCssClass("o_sel_feed_item_edit"); editButton.setEnabled(edit); editButton.setVisible(edit); String deleteId = "delete.".concat(guid); Link deleteButton = LinkFactory.createCustomLink(deleteId, deleteId, "delete", Link.BUTTON_SMALL, vcItems, this); deleteButton.setElementCssClass("o_sel_feed_item_delete"); deleteButton.setEnabled(delete); deleteButton.setVisible(delete); if(feedResource.isInternal() && getIdentity().getKey() != null && getIdentity().getKey().equals(item.getAuthorKey())) { String businessPath = BusinessControlFactory.getInstance().getAsString(getWindowControl().getBusinessControl()); businessPath += "[item=" + item.getGuid() + ":0]"; if(portfolioModule.isEnabled()) { String name = "feed.artefact.item.".concat(guid); BlogEntryMedia media = new BlogEntryMedia(feed, item); MediaCollectorComponent collectorCmp = new MediaCollectorComponent(name, getWindowControl(), media, blogMediaHandler, businessPath); vcItems.put(name, collectorCmp); } else { Controller artefactCtrl = EPUIFactory.createArtefactCollectWizzardController(ureq, getWindowControl(), feedResource, businessPath); if(artefactCtrl != null) { artefactLinks.put(item, artefactCtrl); vcItems.put("feed.artefact.item.".concat(guid), artefactCtrl.getInitialComponent()); } } } editButton.setUserObject(item); deleteButton.setUserObject(item); editButtons.add(editButton); deleteButtons.add(deleteButton); } /** * @see org.olat.core.gui.control.DefaultController#doDispose() */ @Override protected void doDispose() { // make sure the lock is released FeedManager.getInstance().releaseLock(lock); // Dispose confirm deletion dialog controller since it isn't listend to. if (confirmDialogCtr != null) { removeAsListenerAndDispose(confirmDialogCtr); } if(artefactLinks != null) { for(Controller ctrl:artefactLinks.values()) { ctrl.dispose(); } artefactLinks.clear(); artefactLinks = null; } } /** * @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) { FeedManager feedManager = FeedManager.getInstance(); // feed for this event and make sure the updated feed object is in the view Feed feed = feedManager.getFeed(feedResource); vcItems.contextPut("feed", feed); if (source == addItemButton) { currentItem = new Item(); currentItem.setDraft(true); currentItem.setAuthorKey(ureq.getIdentity().getKey()); // Generate new GUID for item, needed for media files that are stored relative to the GUID currentItem.setGuid(CodeHelper.getGlobalForeverUniqueID()); // Create item and media containers feedManager.createItemContainer(feed, currentItem); itemFormCtr = uiFactory.createItemFormController(ureq, getWindowControl(), currentItem, feed); activateModalDialog(itemFormCtr, uiFactory.getTranslator().translate("feed.edit.item")); } else if (editButtons != null && editButtons.contains(source)) { currentItem = (Item) ((Link) source).getUserObject(); // check if still available, maybe deleted by other user in the meantime if (feed.getItems().contains(currentItem)) { lock = feedManager.acquireLock(feed, currentItem, getIdentity()); if (lock.isSuccess()) { // reload to prevent stale object, then launch editor currentItem = feedManager.getItem(feed, currentItem.getGuid()); itemFormCtr = uiFactory.createItemFormController(ureq, getWindowControl(), currentItem, feed); activateModalDialog(itemFormCtr, uiFactory.getTranslator().translate("feed.edit.item")); } else { String fullName = userManager.getUserDisplayName(lock.getOwner()); showInfo("feed.item.is.being.edited.by", fullName); } } else { showInfo("feed.item.is.being.edited.by", "unknown"); } } else if (deleteButtons != null && deleteButtons.contains(source)) { Item item = (Item) ((Link) source).getUserObject(); confirmDialogCtr = activateYesNoDialog(ureq, null, translate("feed.item.confirm.delete"), confirmDialogCtr); confirmDialogCtr.setUserObject(item); } else if (itemLinks != null && itemLinks.contains(source)) { Item item = (Item) ((Link) source).getUserObject(); // Reload first, could be stale item = feedManager.getItem(feed, item.getGuid()); if(item != null) { displayItemController(ureq, item); } } else if (source == makeInternalButton) { if (feed.isUndefined()) { feedManager.updateFeedMode(Boolean.FALSE, feed); } else if (feed.isExternal()) { // Very special case: another user concurrently changed feed to external. Do nothing vcItems.setDirty(true); return; } // else nothing to do, already set to internal by a concurrent user // Add temporary item and open edit dialog addItemButton = LinkFactory.createButton("feed.add.item", vcItems, this); addItemButton.setElementCssClass("o_sel_feed_item_new"); currentItem = new Item(); currentItem.setDraft(true); currentItem.setAuthorKey(ureq.getIdentity().getKey()); // Generate new GUID for item, needed for media files that are stored relative to the GUID currentItem.setGuid(CodeHelper.getGlobalForeverUniqueID()); // Create item and media containers feedManager.createItemContainer(feed, currentItem); itemFormCtr = uiFactory.createItemFormController(ureq, getWindowControl(), currentItem, feed); activateModalDialog(itemFormCtr, uiFactory.getTranslator().translate("feed.edit.item")); // do logging ThreadLocalUserActivityLogger.log(FeedLoggingAction.FEED_EDIT, getClass(), LoggingResourceable.wrap(feed)); } else if (source == makeExternalButton) { if (feed.isUndefined()) { feedManager.updateFeedMode(Boolean.TRUE, feed); vcItems.setDirty(true); // Ask listening FeedMainController to open and handle a new external // feed dialog. fireEvent(ureq, HANDLE_NEW_EXTERNAL_FEED_DIALOG_EVENT); // do logging ThreadLocalUserActivityLogger.log(FeedLoggingAction.FEED_EDIT, getClass(), LoggingResourceable.wrap(feed)); } // else nothing to do, already set to external by a concurrent user } else if (source == olderItemsLink) { helper.olderItems(); createEditButtons(ureq, feed); createCommentsAndRatingsLinks(ureq, feed); vcItems.setDirty(true); } else if (source == newerItemsLink) { helper.newerItems(); createEditButtons(ureq, feed); createCommentsAndRatingsLinks(ureq, feed); vcItems.setDirty(true); } else if (source == startpageLink) { helper.startpage(); createEditButtons(ureq, feed); createCommentsAndRatingsLinks(ureq, feed); vcItems.setDirty(true); } else if (source instanceof Link) { // if it's a link try to get attached identity and assume that user wants // to see the users home page Link userLink = (Link) source; Object userObject = userLink.getUserObject(); if (userObject instanceof Identity) { Identity chosenIdentity = (Identity) userObject; String bPath = "[HomePage:" + chosenIdentity.getKey() + "]"; NewControllerFactory.getInstance().launch(bPath, ureq, getWindowControl()); } } // Check if someone else added an item, reload everything if (!isSameAllItems(feed.getFilteredItems(callback, ureq.getIdentity()))) { resetItems(ureq, feed); } } /** * @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) { FeedManager feedManager = FeedManager.getInstance(); // reload feed for this event and make sure the updated feed object is in the view Feed feed = feedManager.getFeed(feedResource); vcItems.contextPut("feed", feed); if (source == cmc) { if (event.equals(CloseableModalController.CLOSE_MODAL_EVENT)) { removeAsListenerAndDispose(cmc); cmc = null; removeAsListenerAndDispose(itemFormCtr); itemFormCtr = null; // Check if this item has ever been added to the feed. If not, remove the temp dir cleanupTmpItemMediaDir(currentItem, feed, feedManager); // If there were no items and the user doesn't want to save the // first item, go back to the decision whether to make the feed // internally or subscribe to an external feed. if (!feed.hasItems()) { feedManager.updateFeedMode(null, feed); makeInternalAndExternalButtons(); } //release lock feedManager.releaseLock(lock); } } else if (source == confirmDialogCtr && DialogBoxUIFactory.isYesEvent(event)) { // The user confirmed that the item shall be deleted Item item = (Item) ((DialogBoxController) source).getUserObject(); lock = feedManager.acquireLock(feed, item, getIdentity()); if (lock.isSuccess()) { // remove the item from the naviCtr naviCtr.remove(item); // remove the item also from the helper (cached selection) helper.removeItem(item); // permanently remove item feed = feedManager.remove(item, feed); // remove delete and edit buttons of this item deleteButtons.remove(source); for (Link editButton : editButtons) { if (item.equals(editButton.getUserObject())) { editButtons.remove(editButton); break; } } // If the last item has been deleted, provide buttons for adding // items manually or from an external source/feed. if (!feed.hasItems()) { makeInternalAndExternalButtons(); // The subscription/feed url from the feed info is obsolete fireEvent(ureq, ItemsController.FEED_INFO_IS_DIRTY_EVENT); } else { if (callback.mayEditItems() || callback.mayCreateItems()) { createEditButtons(ureq, feed); } createCommentsAndRatingsLinks(ureq, feed); } vcItems.setDirty(true); // in case we were in single item view, show all items mainPanel.setContent(vcItems); feedManager.releaseLock(lock); lock = null; // do logging ThreadLocalUserActivityLogger.log(FeedLoggingAction.FEED_ITEM_DELETE, getClass(), LoggingResourceable.wrap(item)); } else { String fullName = userManager.getUserDisplayName(lock.getOwner()); showInfo("feed.item.is.being.edited.by", fullName); } } else if (source == itemFormCtr) { if (event.equals(Event.CHANGED_EVENT) || event.equals(Event.CANCELLED_EVENT)) { if (event.equals(Event.CHANGED_EVENT)) { FileElement mediaFile = currentItem.getMediaFile(); if (feedManager.getItemContainer(currentItem, feed) == null) { // Ups, deleted in the meantime by someone else // remove the item from the naviCtr naviCtr.remove(currentItem); // remove the item also from the helper (cached selection) helper.removeItem(currentItem); } else { if (!feed.getItems().contains(currentItem)) { // Add the modified item if it is not part of the feed feed = feedManager.addItem(currentItem, mediaFile, feed); if(feed == null) { //the item could not be added, is not internal feed = feedManager.getFeed(feedResource); if(!feed.isInternal() && !feed.isExternal() && !feed.hasItems()) { feed = feedManager.updateFeedMode(Boolean.FALSE, feed); feed = feedManager.addItem(currentItem, mediaFile, feed); } } if(feed != null) { createButtonsForItem(ureq, feed, currentItem); createItemLink(currentItem); // Add date component String guid = currentItem.getGuid(); if(currentItem.getDate() != null) { DateComponentFactory.createDateComponentWithYear("date." + guid, currentItem.getDate(), vcItems); } // Add comments and rating createCommentsAndRatingsLink(ureq, feed, currentItem); // add it to the navigation controller naviCtr.add(currentItem); // ... and also to the helper helper.addItem(currentItem); if (feed.getItems() != null && feed.getItems().size() == 1) { // First item added, show feed url (for subscription) fireEvent(ureq, ItemsController.FEED_INFO_IS_DIRTY_EVENT); // Set the base URI of the feed for the current user. All users // have unique URIs. helper.setURIs(); } // do logging ThreadLocalUserActivityLogger.log(FeedLoggingAction.FEED_ITEM_CREATE, getClass(), LoggingResourceable.wrap(currentItem)); } } else { // Write item file feed = feedManager.updateItem(currentItem, mediaFile, feed); // Update current item in the users view, replace in helper cache of // current selected items. helper.updateItem(currentItem); // Do logging ThreadLocalUserActivityLogger.log(FeedLoggingAction.FEED_ITEM_EDIT, getClass(), LoggingResourceable.wrap(currentItem)); } } vcItems.setDirty(true); // if the current item is displayed, update the view if (itemCtr != null) { itemCtr.getInitialComponent().setDirty(true); } } else if (event.equals(Event.CANCELLED_EVENT)) { // Check if this item has ever been added to the feed. If not, remove the temp dir cleanupTmpItemMediaDir(currentItem, feed, feedManager); // If there were no items and the user doesn't want to save the // first item, go back to the decision whether to make the feed // internally or subscribe to an external feed. if (!feed.hasItems()) { feedManager.updateFeedMode(null, feed); makeInternalAndExternalButtons(); } } // release the lock feedManager.releaseLock(lock); // Dispose the cmc and the podcastFormCtr. cmc.deactivate(); removeAsListenerAndDispose(cmc); cmc = null; removeAsListenerAndDispose(itemFormCtr); itemFormCtr = null; } } else if (source == naviCtr && event instanceof NavigationEvent) { List<? extends Dated> selItems = ((NavigationEvent) event).getSelectedItems(); List<Item> items = new ArrayList<Item>(); for (Dated item : selItems) { if (item instanceof Item) { items.add((Item) item); } } // make sure items are sorted properly Collections.sort(items, new ItemPublishDateComparator()); helper.setSelectedItems(items); if (callback.mayEditItems() || callback.mayCreateItems()) { createEditButtons(ureq, feed); } createCommentsAndRatingsLinks(ureq, feed); vcItems.setDirty(true); mainPanel.setContent(vcItems); } else if (source == itemCtr) { if (event == Event.BACK_EVENT) { mainPanel.setContent(vcItems); } } else if (source instanceof UserCommentsAndRatingsController) { UserCommentsAndRatingsController commentsRatingsCtr = (UserCommentsAndRatingsController) source; if (event == UserCommentsAndRatingsController.EVENT_COMMENT_LINK_CLICKED) { // go to details page Item item = (Item) commentsRatingsCtr.getUserObject(); item = feedManager.getItem(feed, item.getGuid()); if(item != null) { ItemController myItemCtr = displayItemController(ureq, item); List<ContextEntry> entries = BusinessControlFactory.getInstance().createCEListFromResourceType(ItemController.ACTIVATION_KEY_COMMENTS); myItemCtr.activate(ureq, entries, null); } } } // Check if someone else added an item, reload everything if (feed == null) { //do something } else if(!isSameAllItems(feed.getFilteredItems(callback, getIdentity()))) { resetItems(ureq, feed); } } /** * Private helper to remove any temp media files created for this feed * @param tmpItem * @param feed * @param feedManager */ private void cleanupTmpItemMediaDir(Item tmpItem, Feed feed, FeedManager feedManager) { // Add GUID null check to not accidentally delete the entire feed directory // in case there is somewhere a programming error if (!feed.getItems().contains(tmpItem) && tmpItem.getGuid() != null) { VFSContainer itemContainer = feedManager.getItemContainer(tmpItem, feed); if (itemContainer != null) { itemContainer.delete(); } } } /** * @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(); } /** * Sets the items view dirty. * @param ureq * @param feed the current feed */ public void resetItems(UserRequest ureq, Feed feed) { FeedManager.getInstance().loadItems(feed); List<Item> items = feed.getFilteredItems(callback, ureq.getIdentity()); helper.setSelectedItems(items); naviCtr.setDatedObjects(items); setAllItemIds(items); // Add item details page link createItemLinks(feed); // Add item user comments link and rating if (displayConfig.isShowCRInMinimized()) { createCommentsAndRatingsLinks(ureq, feed); } // Add date components createDateComponents(feed); vcItems.setDirty(true); } /** * Displays the item in the mainPanel of this controller. * * @param ureq * @param item */ private ItemController displayItemController(UserRequest ureq, Item item) { removeAsListenerAndDispose(itemCtr); Link editButton = getButtonByUserObject(item, editButtons); Link deleteButton = getButtonByUserObject(item, deleteButtons); Controller artefactLink = getArtefactLinkByUserObject(item); FeedManager feedManager = FeedManager.getInstance(); Feed feed = feedManager.getFeed(feedResource); itemCtr = new ItemController(ureq, getWindowControl(), item, feed, helper, uiFactory, callback, editButton, deleteButton, artefactLink, displayConfig); listenTo(itemCtr); mainPanel.setContent(itemCtr.getInitialComponent()); return itemCtr; } @Override public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { if(entries == null || entries.isEmpty() || feedResource == null) return; String itemId = entries.get(0).getOLATResourceable().getResourceableTypeName(); if(itemId != null && itemId.startsWith("item=")) { itemId = itemId.substring(5, itemId.length()); } int index = feedResource.getItemIds().indexOf(itemId); if (index >= 0) { Item item = feedResource.getItems().get(index); activate(ureq, item); } } /** * @param item */ public void activate(UserRequest ureq, Item item) { displayItemController(ureq, item); } /** * @param item * @param buttons * @return The Link in buttons which has the item attached as user object or * null */ private Link getButtonByUserObject(Item item, List<Link> buttons) { Link result = null; if (buttons != null && item != null) { for (Link button : buttons) { if (item.equals(button.getUserObject())) { result = button; break; } } } return result; } private Controller getArtefactLinkByUserObject(Item item) { Controller result = null; if (artefactLinks != null && artefactLinks.containsKey(item)) { return artefactLinks.get(item); } return result; } private class ItemId { private final String guid; private final Date lastModification; public ItemId(Item item) { guid = item.getGuid(); lastModification = item.getLastModified(); } @Override public int hashCode() { return guid.hashCode() + (lastModification == null ? -483 : lastModification.hashCode()); } @Override public boolean equals(Object obj) { if(this == obj) { return true; } if(obj instanceof ItemId) { ItemId id = (ItemId)obj; return guid.equals(id.guid) && ((lastModification == null && id.lastModification == null) || (lastModification != null && lastModification.equals(id.lastModification))); } return false; } } }