001/* 002 * Copyright 2010 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.odfweb.repository; 017 018import java.util.Arrays; 019import java.util.Collection; 020import java.util.Iterator; 021import java.util.List; 022import java.util.stream.Collectors; 023import java.util.stream.Stream; 024 025import org.apache.commons.lang3.StringUtils; 026 027import org.ametys.odf.ProgramItem; 028import org.ametys.odf.course.Course; 029import org.ametys.odf.courselist.CourseList; 030import org.ametys.odf.program.Program; 031import org.ametys.plugins.odfweb.repository.ProgramPage.AbstractTreeIterator; 032import org.ametys.plugins.repository.AmetysObject; 033import org.ametys.plugins.repository.AmetysObjectIterable; 034import org.ametys.plugins.repository.AmetysRepositoryException; 035import org.ametys.plugins.repository.CollectionIterable; 036import org.ametys.plugins.repository.UnknownAmetysObjectException; 037import org.ametys.plugins.repository.data.holder.ModelLessDataHolder; 038import org.ametys.plugins.repository.data.holder.impl.DefaultModelLessDataHolder; 039import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 040import org.ametys.plugins.repository.data.repositorydata.impl.MemoryRepositoryData; 041import org.ametys.web.repository.page.Page; 042import org.ametys.web.repository.page.virtual.VirtualPageConfiguration; 043 044import com.google.common.collect.Iterables; 045 046/** 047 * Page representing a course. 048 */ 049public class CoursePage extends AbstractProgramItemPage<CoursePageFactory> 050{ 051 private Course _course; 052 private String _path; 053 private Page _parentPage; 054 private Program _parentProgram; 055 056 /** 057 * Constructor. 058 * @param factory The factory 059 * @param root the odf root page. 060 * @param course the course. 061 * @param parentProgram the parent program 062 * @param path path from the parent {@link ProgramPage} 063 * @param parentPage the parent {@link Page} or null if not yet computed. 064 * @param configuration The Course virtual page's configuration 065 */ 066 public CoursePage(Page root, VirtualPageConfiguration configuration, CoursePageFactory factory, Course course, Program parentProgram, String path, Page parentPage) 067 { 068 super(root, configuration, factory.getScheme(), factory); 069 070 _course = course; 071 _path = path; 072 _parentPage = parentPage; 073 _parentProgram = parentProgram; 074 } 075 076 /** 077 * Returns the associated {@link Course}. 078 * @return the associated {@link Course}. 079 */ 080 public Course getCourse() 081 { 082 return _course; 083 } 084 085 @Override 086 protected ProgramItem getProgramItem() 087 { 088 return getCourse(); 089 } 090 091 @Override 092 public int getDepth() throws AmetysRepositoryException 093 { 094 int levelDepth = 0; 095 if (StringUtils.isNotBlank(_factory.getODFPageHandler().getLevel1Metadata(_root))) 096 { 097 levelDepth++; 098 if (StringUtils.isNotBlank(_factory.getODFPageHandler().getLevel2Metadata(_root))) 099 { 100 levelDepth++; 101 } 102 } 103 104 return _root.getDepth() + levelDepth + _path.split("/").length; 105 } 106 107 @Override 108 public String getTitle() throws AmetysRepositoryException 109 { 110 return _course.getTitle(); 111 } 112 113 @Override 114 public String getLongTitle() throws AmetysRepositoryException 115 { 116 return _course.getTitle(); 117 } 118 119 @Override 120 public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException 121 { 122 List<Page> coursePages = _traverseCourseLists().map(this::_createCoursePage).collect(Collectors.toList()); 123 return new CollectionIterable<>(coursePages); 124 } 125 126 private CoursePage _createCoursePage(Course course) 127 { 128 return _factory.createCoursePage(_root, course, _parentProgram, _path + '/' + getName(), this); 129 } 130 131 @Override 132 public String getPathInSitemap() throws AmetysRepositoryException 133 { 134 String computeLevelsPath = _factory.getODFPageHandler().computeLevelsPath(_root, _parentProgram); 135 String path = StringUtils.isNotBlank(computeLevelsPath) ? computeLevelsPath + "/" + _path + "/" + getName() : _path + "/" + getName(); 136 return _root.getPathInSitemap() + "/" + path; 137 } 138 139 @SuppressWarnings("unchecked") 140 @Override 141 public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 142 { 143 if (path.isEmpty()) 144 { 145 throw new AmetysRepositoryException("path must be non empty"); 146 } 147 148 List<String> headQueuePath = Arrays.asList(StringUtils.split(path, "/", 2)); 149 String name = headQueuePath.get(0); 150 String queuePath = Iterables.get(headQueuePath, 1, null); 151 152 return (A) _traverseCourseLists() 153 .filter(course -> _filterByPageName(course, name)) 154 .findFirst() 155 .map(this::_createCoursePage) 156 .map(cp -> _factory.getODFPageHandler().addRedirectIfNeeded(cp, name)) 157 .map(cp -> _factory.getODFPageHandler().exploreQueuePath(cp, queuePath)) 158 .orElseThrow(() -> new UnknownAmetysObjectException("Unknown child page '" + path + "' for page " + getId())); 159 } 160 161 private boolean _filterByPageName(Course course, String pageName) 162 { 163 // If last part is equals to the course code, the page matches 164 return course.getCode().equals(pageName.substring(pageName.lastIndexOf("-") + 1)); 165 } 166 167 @Override 168 public boolean hasChild(String name) throws AmetysRepositoryException 169 { 170 return _traverseCourseLists() 171 .filter(course -> _filterByPageName(course, name)) 172 .findFirst() 173 .isPresent(); 174 } 175 176 @Override 177 public String getId() throws AmetysRepositoryException 178 { 179 // E.g: course://licence-lea-anglais-allemand-H6YOLDDJ/parcours-1-f7usj1ss?rootId=xxx&courseId=xxx&programId=xxxx 180 return "course://" + _path + "?rootId=" + _root.getId() + "&courseId=" + _course.getId() + "&programId=" + _parentProgram.getId(); 181 } 182 @Override 183 public String getName() throws AmetysRepositoryException 184 { 185 // E.g: langue-anglaise-1-H6YP1P98 186 return _factory.getODFPageHandler().getPageName(_course); 187 } 188 189 @SuppressWarnings("unchecked") 190 @Override 191 public Page getParent() throws AmetysRepositoryException 192 { 193 if (_parentPage == null) 194 { 195 String computeLevelsPath = _factory.getODFPageHandler().computeLevelsPath(_root, _parentProgram); 196 String relParentPath = StringUtils.isNotBlank(computeLevelsPath) ? computeLevelsPath + "/" + _path : _path; 197 _parentPage = _root.getChild(relParentPath); 198 } 199 200 return _parentPage; 201 } 202 203 @Override 204 public String getParentPath() throws AmetysRepositoryException 205 { 206 String computeLevelsPath = _factory.getODFPageHandler().computeLevelsPath(_root, _parentProgram); 207 String relParentPath = StringUtils.isNotBlank(computeLevelsPath) ? computeLevelsPath + "/" + _path : _path; 208 209 return _root.getPath() + "/" + relParentPath; 210 } 211 212 public ModelLessDataHolder getDataHolder() 213 { 214 RepositoryData repositoryData = new MemoryRepositoryData(getName()); 215 return new DefaultModelLessDataHolder(_factory.getPageDataTypeEP(), repositoryData); 216 } 217 218 private Stream<Course> _traverseCourseLists() 219 { 220 CourseListTraverser traverser = new CourseListTraverser(_course.getCourseLists()); 221 return traverser.stream() 222 .filter(Course.class::isInstance) 223 .map(Course.class::cast); 224 } 225 226 static class CourseListTraverser extends AbstractTreeIterator<ProgramItem> 227 { 228 public CourseListTraverser(Collection<? extends ProgramItem> programPartChildren) 229 { 230 super(programPartChildren); 231 } 232 233 @Override 234 protected Iterator<ProgramItem> provideChildIterator(ProgramItem parent) 235 { 236 if (parent instanceof CourseList courseList) 237 { 238 return new CourseListTraverser(courseList.getCourses()); 239 } 240 241 return null; 242 } 243 } 244 245 @SuppressWarnings("unchecked") 246 @Override 247 public Course getContent() 248 { 249 Course course = getCourse(); 250 course.setContextPath(getPathInSitemap()); 251 252 setCurrentEducationalPaths(course); 253 254 return course; 255 } 256}