/** * <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.video.ui; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.olat.core.commons.persistence.SortKey; import org.olat.core.dispatcher.mapper.Mapper; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; import org.olat.core.gui.components.form.flexible.elements.FlexiTableSort; import org.olat.core.gui.components.form.flexible.elements.FlexiTableSortOptions; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableCssDelegate; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponentDelegate; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableRendererType; import org.olat.core.gui.components.stack.TooledStackedPanel; 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.generic.dtabs.Activateable2; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.media.NotFoundMediaResource; 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.core.util.vfs.VFSMediaResource; import org.olat.fileresource.types.VideoFileResource; import org.olat.modules.video.ui.VideoEntryDataModel.Cols; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; import org.olat.repository.RepositoryModule; import org.olat.repository.RepositoryService; import org.olat.repository.model.SearchMyRepositoryEntryViewParams; import org.olat.repository.model.SearchMyRepositoryEntryViewParams.OrderBy; import org.olat.util.logging.activity.LoggingResourceable; import org.springframework.beans.factory.annotation.Autowired; /** * This site implements a YouTube stile video library for self-study. * * Initial date: 08.05.2016<br> * * @author gnaegi, gnaegi@frentix.com, http://www.frentix.com * */ public class VideoListingController extends FormBasicController implements Activateable2, FlexiTableComponentDelegate { private final TooledStackedPanel toolbarPanel; private final String imgUrl; private FlexiTableElement tableEl; private VideoEntryDataModel model; private VideoEntryDataSource dataSource; private SearchMyRepositoryEntryViewParams searchParams; @Autowired private RepositoryModule repositoryModule; @Autowired private RepositoryManager repositoryManager; @Autowired private RepositoryService repositoryService; public VideoListingController(UserRequest ureq, WindowControl wControl, TooledStackedPanel toolbarPanel) { super(ureq, wControl, "video_listing"); this.setTranslator(Util.createPackageTranslator(RepositoryService.class, getLocale(), getTranslator())); this.toolbarPanel = toolbarPanel; searchParams = new SearchMyRepositoryEntryViewParams(getIdentity(), ureq.getUserSession().getRoles(), VideoFileResource.TYPE_NAME); dataSource = new VideoEntryDataSource(searchParams); imgUrl = registerMapper(ureq, new VideoMapper()); initForm(ureq); tableEl.reloadData(); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, Cols.key.i18nKey(), Cols.key.ordinal(), true, OrderBy.key.name())); model = new VideoEntryDataModel(dataSource, columnsModel); tableEl = uifactory.addTableElement(getWindowControl(), "table", model, 20, false, getTranslator(), formLayout); tableEl.setAvailableRendererTypes(FlexiTableRendererType.custom); tableEl.setRendererType(FlexiTableRendererType.custom); tableEl.setSearchEnabled(true); tableEl.setCustomizeColumns(false); tableEl.setElementCssClass("o_video_listing"); tableEl.setEmtpyTableMessageKey("table.sEmptyTable"); tableEl.setPageSize(24); VelocityContainer row = createVelocityContainer("video_cell"); row.contextPut("imgUrl", imgUrl); row.setDomReplacementWrapperRequired(false); // sets its own DOM id in velocity container tableEl.setRowRenderer(row, this); tableEl.setCssDelegate(new VideoCssDelegate()); initSorters(tableEl); tableEl.setAndLoadPersistedPreferences(ureq, "video-list"); } private void initSorters(FlexiTableElement tableElement) { List<FlexiTableSort> sorters = new ArrayList<>(8); sorters.add(new FlexiTableSort(translate("orderby.automatic"), OrderBy.automatic.name())); sorters.add(new FlexiTableSort(translate("orderby.title"), OrderBy.title.name())); sorters.add(new FlexiTableSort(translate("orderby.author"), OrderBy.author.name())); sorters.add(new FlexiTableSort(translate("orderby.creationDate"), OrderBy.creationDate.name())); sorters.add(new FlexiTableSort(translate("orderby.launchCounter"), OrderBy.launchCounter.name())); if(repositoryModule.isRatingEnabled()) { sorters.add(new FlexiTableSort(translate("orderby.rating"), OrderBy.rating.name())); } FlexiTableSortOptions options = new FlexiTableSortOptions(sorters); options.setDefaultOrderBy(new SortKey(OrderBy.creationDate.name(), false)); tableElement.setSortSettings(options); } @Override public Iterable<Component> getComponents(int row, Object rowObject) { return null; } @Override protected void formOK(UserRequest ureq) { // } /** * Launch a single video and add to breadcrumb * @param ureq * @param id the video resource ID */ private void doShowVideo(UserRequest ureq, Long id) { RepositoryEntry videoEntry = repositoryManager.lookupRepositoryEntry(id); if (repositoryManager.isAllowed(ureq, videoEntry).canLaunch()) { boolean readOnly = repositoryManager.createRepositoryEntryStatus(videoEntry.getStatusCode()).isClosed(); VideoDisplayController videoDisplayCtr = new VideoDisplayController(ureq, getWindowControl(), videoEntry, true, true, true, true, null, false, true, null, readOnly); listenTo(videoDisplayCtr); toolbarPanel.pushController(videoEntry.getDisplayname(), videoDisplayCtr); // Update launch counter repositoryService.incrementLaunchCounter(videoEntry); // Update business path and URL ContextEntry ce = BusinessControlFactory.getInstance().createContextEntry(videoEntry); ThreadLocalUserActivityLogger.addLoggingResourceInfo(LoggingResourceable.wrapBusinessPath(ce.getOLATResourceable())); WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ce, getWindowControl()); addToHistory(ureq, bwControl); } } @Override public void event(UserRequest ureq, Component source, Event event) { if(source == mainForm.getInitialComponent()) { if("ONCLICK".equals(event.getCommand())) { String rowKeyStr = ureq.getParameter("select_row"); if(StringHelper.isLong(rowKeyStr)) { try { doShowVideo(ureq, new Long(rowKeyStr)); } catch (NumberFormatException e) { logWarn("Not a valid long: " + rowKeyStr, e); } } } } } @Override public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { // cleanup existing video if (toolbarPanel.getBreadCrumbs().size() > 1) { toolbarPanel.popUpToRootController(ureq); } if(entries == null || entries.isEmpty()) { // nothing to do, default video listing } else { Long id = entries.get(0).getOLATResourceable().getResourceableId(); doShowVideo(ureq, id); } } @Override protected void doDispose() { // controllers auto-disposed by basic controller } public class VideoMapper implements Mapper { @Override public MediaResource handle(String relPath, HttpServletRequest request) { if (StringHelper.containsNonWhitespace(relPath)) { int start = relPath.lastIndexOf("/"); if (start != -1) { relPath = relPath.substring(start+1); long id = Long.parseLong(relPath); OLATResourceable videoResource = OresHelper.createOLATResourceableInstance("RepositoryEntry", id); VFSLeaf imageFile = repositoryManager.getImage(videoResource); return new VFSMediaResource(imageFile); } } return new NotFoundMediaResource("Image for resource ID::" + relPath + " not found"); } } private static class VideoCssDelegate extends DefaultFlexiTableCssDelegate { @Override public String getRowCssClass(FlexiTableRendererType type, int pos) { return "o_video_entry"; } } }