/**
* <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.repository.ui.catalog;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.olat.core.dispatcher.mapper.MapperService;
import org.olat.core.dispatcher.mapper.manager.MapperKey;
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.panel.MainPanel;
import org.olat.core.gui.components.stack.BreadcrumbedStackedPanel;
import org.olat.core.gui.components.velocity.VelocityContainer;
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.dtabs.Activateable2;
import org.olat.core.id.OLATResourceable;
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.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.resource.OresHelper;
import org.olat.core.util.vfs.VFSLeaf;
import org.olat.repository.CatalogEntry;
import org.olat.repository.CatalogEntry.Style;
import org.olat.repository.RepositoryManager;
import org.olat.repository.manager.CatalogManager;
import org.olat.repository.model.SearchMyRepositoryEntryViewParams;
import org.olat.repository.ui.CatalogEntryImageMapper;
import org.olat.repository.ui.list.RepositoryEntryListController;
import org.olat.util.logging.activity.LoggingResourceable;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* Initial date: 28.01.2014<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class CatalogNodeController extends BasicController implements Activateable2 {
private CatalogNodeController childNodeController;
private BreadcrumbedStackedPanel stackPanel;
private RepositoryEntryListController entryListController;
private RepositoryEntryListController closedEntryListController;
private final boolean wrapInMainPanel;
private final MapperKey mapperThumbnailKey;
private final WindowControl rootwControl;
@Autowired
private MapperService mapperService;
@Autowired
private CatalogManager catalogManager;
public CatalogNodeController(UserRequest ureq, WindowControl wControl, WindowControl rootwControl,
CatalogEntry catalogEntry, BreadcrumbedStackedPanel stackPanel, boolean wrapInMainPanel) {
// fallback translator to repository package to reduce redundant translations
super(ureq, wControl, Util.createPackageTranslator(RepositoryManager.class, ureq.getLocale()));
this.stackPanel = stackPanel;
this.rootwControl = rootwControl;
this.wrapInMainPanel = wrapInMainPanel;
VelocityContainer mainVC = createVelocityContainer("node");
mainVC.setDomReplacementWrapperRequired(false); // uses own DOM ID
//one mapper for all users
mapperThumbnailKey = mapperService.register(null, "catalogentryImage", new CatalogEntryImageMapper());
mainVC.contextPut("mapperThumbnailUrl", mapperThumbnailKey.getUrl());
if(catalogEntry.getStyle() != null) {
mainVC.contextPut("listStyle", catalogEntry.getStyle().name());
} else {
mainVC.contextPut("listStyle", Style.tiles.name());
}
mainVC.contextPut("catalogEntryName", catalogEntry.getName());
int level = 0;
CatalogEntry parent = catalogEntry.getParent();
while (parent != null) {
level++;
parent = parent.getParent();
}
mainVC.contextPut("catalogLevel", level);
if(StringHelper.containsNonWhitespace(catalogEntry.getDescription())) {
mainVC.contextPut("catalogEntryDesc", catalogEntry.getDescription());
}
VFSLeaf image = catalogManager.getImage(catalogEntry);
if(image != null) {
mainVC.contextPut("catThumbnail", image.getName());
}
List<CatalogEntry> childCe = catalogManager.getNodesChildrenOf(catalogEntry);
Collections.sort(childCe, new CatalogEntryComparator(getLocale()));
List<String> subCategories = new ArrayList<>();
int count = 0;
for (CatalogEntry entry : childCe) {
if(entry.getType() == CatalogEntry.TYPE_NODE) {
String cmpId = "cat_" + (++count);
VFSLeaf img = catalogManager.getImage(entry);
if(img != null) {
String imgId = "image_" + count;
mainVC.contextPut(imgId, img.getName());
}
mainVC.contextPut("k" + cmpId, entry.getKey());
String title = StringHelper.escapeHtml(entry.getName());
Link link = LinkFactory.createCustomLink(cmpId, "select_node", cmpId, Link.LINK + Link.NONTRANSLATED, mainVC, this);
link.setCustomDisplayText(title);
link.setIconLeftCSS("o_icon o_icon_catalog_sub");
link.setUserObject(entry.getKey());
subCategories.add(Integer.toString(count));
String titleId = "title_" + count;
mainVC.contextPut(titleId, title);
}
}
mainVC.contextPut("subCategories", subCategories);
//catalog resources
SearchMyRepositoryEntryViewParams searchParams
= new SearchMyRepositoryEntryViewParams(getIdentity(), ureq.getUserSession().getRoles());
searchParams.setParentEntry(catalogEntry);
searchParams.setClosed(Boolean.FALSE);
entryListController = new RepositoryEntryListController(ureq, wControl, searchParams, true, false, "catalog", stackPanel);
if(!entryListController.isEmpty()) {
mainVC.put("entries", entryListController.getInitialComponent());
}
listenTo(entryListController);
//catalog closed resources
SearchMyRepositoryEntryViewParams searchClosedParams
= new SearchMyRepositoryEntryViewParams(getIdentity(), ureq.getUserSession().getRoles());
searchClosedParams.setParentEntry(catalogEntry);
searchClosedParams.setClosed(Boolean.TRUE);
closedEntryListController = new RepositoryEntryListController(ureq, wControl, searchClosedParams, true, false, "catalog-closed", stackPanel);
if(!closedEntryListController.isEmpty()) {
mainVC.put("closedEntries", closedEntryListController.getInitialComponent());
}
listenTo(closedEntryListController);
if(wrapInMainPanel) {
MainPanel mainPanel = new MainPanel("myCoursesMainPanel");
mainPanel.setContent(mainVC);
putInitialPanel(mainPanel);
} else {
putInitialPanel(mainVC);
}
}
@Override
public void event(UserRequest ureq, Component source, Event event) {
if(source instanceof Link) {
Link link = (Link)source;
if("select_node".equals(link.getCommand())) {
Long categoryNodeKey = (Long)link.getUserObject();
CatalogEntry entry = catalogManager.getCatalogNodeByKey(categoryNodeKey);
selectCatalogEntry(ureq, entry);
}
} else if("img_select".equals(event.getCommand())) {
String node = ureq.getParameter("node");
if(StringHelper.isLong(node)) {
try {
Long categoryNodeKey = new Long(node);
CatalogEntry entry = catalogManager.getCatalogNodeByKey(categoryNodeKey);
selectCatalogEntry(ureq, entry);
} catch (NumberFormatException e) {
logWarn("Not a valid long: " + node, e);
}
}
}
}
private CatalogNodeController selectCatalogEntry(UserRequest ureq, CatalogEntry entry) {
if(entry != null && entry.getType() == CatalogEntry.TYPE_NODE) {
removeAsListenerAndDispose(childNodeController);
OLATResourceable ores = OresHelper.createOLATResourceableInstance("CatalogEntry", entry.getKey());
ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ores));
WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, rootwControl);
childNodeController = new CatalogNodeController(ureq, bwControl, rootwControl, entry, stackPanel, wrapInMainPanel);
listenTo(childNodeController);
stackPanel.pushController(entry.getName(), childNodeController);
addToHistory(ureq, childNodeController);
}
return childNodeController;
}
@Override
protected void doDispose() {
//
}
@Override
public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
if(entries == null || entries.isEmpty()) {
return;
}
ContextEntry entry = entries.get(0);
String type = entry.getOLATResourceable().getResourceableTypeName();
if("CatalogEntry".equalsIgnoreCase(type)) {
Long entryKey = entry.getOLATResourceable().getResourceableId();
if(entryKey != null && entryKey.longValue() > 0) {
activateRoot(ureq, entryKey);
}
} else if("Node".equalsIgnoreCase(type)) {
//the "Node" is only for internal usage
StateEntry stateEntry = entry.getTransientState();
if(stateEntry instanceof CatalogStateEntry) {
CatalogEntry catalogEntry = ((CatalogStateEntry)stateEntry).getEntry();
CatalogNodeController nextCtrl = selectCatalogEntry(ureq, catalogEntry);
if(nextCtrl != null && entries.size() > 1) {
nextCtrl.activate(ureq, entries.subList(1, entries.size()), null);
}
}
}
}
/**
* Build an internal business path made of "Node" with the category
* as state entry to prevent loading several times the same entries.
*
* @param ureq
* @param entryKey
*/
private void activateRoot(UserRequest ureq, Long entryKey) {
List<ContextEntry> parentLine = new ArrayList<>();
for(CatalogEntry node = catalogManager.getCatalogEntryByKey(entryKey); node != null && node.getParent() != null; node=node.getParent()) {
OLATResourceable nodeRes = OresHelper.createOLATResourceableInstance("Node", node.getKey());
ContextEntry ctxEntry = BusinessControlFactory.getInstance().createContextEntry(nodeRes);
ctxEntry.setTransientState(new CatalogStateEntry(node));
parentLine.add(ctxEntry);
}
Collections.reverse(parentLine);
activate(ureq, parentLine, null);
}
}