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.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.Iterator; 022import java.util.List; 023import java.util.stream.Collectors; 024import java.util.stream.Stream; 025 026import org.apache.commons.lang3.StringUtils; 027 028import org.ametys.odf.ProgramItem; 029import org.ametys.odf.course.Course; 030import org.ametys.odf.courselist.CourseList; 031import org.ametys.odf.program.Program; 032import org.ametys.plugins.odfweb.repository.ProgramPage.AbstractTreeIterator; 033import org.ametys.plugins.repository.AmetysObject; 034import org.ametys.plugins.repository.AmetysObjectIterable; 035import org.ametys.plugins.repository.AmetysRepositoryException; 036import org.ametys.plugins.repository.CollectionIterable; 037import org.ametys.plugins.repository.UnknownAmetysObjectException; 038import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 039import org.ametys.plugins.repository.data.holder.ModelLessDataHolder; 040import org.ametys.plugins.repository.data.holder.impl.DefaultModelLessDataHolder; 041import org.ametys.plugins.repository.data.repositorydata.RepositoryData; 042import org.ametys.plugins.repository.data.repositorydata.impl.MemoryRepositoryData; 043import org.ametys.web.repository.page.Page; 044import org.ametys.web.repository.page.UnknownZoneException; 045import org.ametys.web.repository.page.Zone; 046import org.ametys.web.repository.site.Site; 047import org.ametys.web.repository.sitemap.Sitemap; 048 049import com.google.common.collect.Iterables; 050 051/** 052 * Page representing a course. 053 */ 054public class CoursePage extends AbstractOdfPage 055{ 056 private Page _root; 057 private Course _course; 058 private String _path; 059 private Page _parentPage; 060 private Program _parentProgram; 061 private CoursePageFactory _factory; 062 063 /** 064 * Constructor. 065 * @param factory The factory 066 * @param root the odf root page. 067 * @param course the course. 068 * @param parentProgram the parent program 069 * @param path path from the parent {@link ProgramPage} 070 * @param parentPage the parent {@link Page} or null if not yet computed. 071 */ 072 public CoursePage(CoursePageFactory factory, Page root, Course course, Program parentProgram, String path, Page parentPage) 073 { 074 _factory = factory; 075 _root = root; 076 _course = course; 077 _path = path; 078 _parentPage = parentPage; 079 _parentProgram = parentProgram; 080 } 081 082 /** 083 * Returns the associated {@link Course}. 084 * @return the associated {@link Course}. 085 */ 086 public Course getCourse() 087 { 088 return _course; 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 getTemplate() throws AmetysRepositoryException 109 { 110 return "course"; 111 } 112 113 @Override 114 public String getTitle() throws AmetysRepositoryException 115 { 116 return _course.getTitle(); 117 } 118 119 @Override 120 public String getLongTitle() throws AmetysRepositoryException 121 { 122 return _course.getTitle(); 123 } 124 125 @Override 126 public Zone getZone(String name) throws UnknownZoneException, AmetysRepositoryException 127 { 128 if (!"default".equals(name)) 129 { 130 throw new IllegalArgumentException("Only the zone named 'default' is actually supported on virtual program pages."); 131 } 132 133 return new CourseZone(this, _factory.getZoneDataTypeEP(), _factory.getZoneItemDataTypeEP()); 134 } 135 136 @Override 137 public AmetysObjectIterable< ? extends Zone> getZones() throws AmetysRepositoryException 138 { 139 ArrayList<Zone> zones = new ArrayList<>(); 140 zones.add(new CourseZone(this, _factory.getZoneDataTypeEP(), _factory.getZoneItemDataTypeEP())); 141 return new CollectionIterable<>(zones); 142 } 143 144 @Override 145 public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException 146 { 147 List<Page> coursePages = _traverseCourseLists().map(this::_toCoursePage).collect(Collectors.toList()); 148 return new CollectionIterable<>(coursePages); 149 } 150 151 private CoursePage _toCoursePage(Course course) 152 { 153 return new CoursePage(_factory, _root, course, _parentProgram, _path + '/' + getName(), this); 154 } 155 156 @Override 157 public String getPathInSitemap() throws AmetysRepositoryException 158 { 159 String computeLevelsPath = _factory.getODFPageHandler().computeLevelsPath(_root, _parentProgram); 160 String path = StringUtils.isNotBlank(computeLevelsPath) ? computeLevelsPath + "/" + _path + "/" + getName() : _path + "/" + getName(); 161 return _root.getPathInSitemap() + "/" + path; 162 } 163 164 @Override 165 public Site getSite() throws AmetysRepositoryException 166 { 167 return _root.getSite(); 168 } 169 170 @Override 171 public String getSiteName() throws AmetysRepositoryException 172 { 173 return _root.getSiteName(); 174 } 175 176 @Override 177 public Sitemap getSitemap() throws AmetysRepositoryException 178 { 179 return _root.getSitemap(); 180 } 181 182 @Override 183 public String getSitemapName() throws AmetysRepositoryException 184 { 185 return _root.getSitemapName(); 186 } 187 188 @SuppressWarnings("unchecked") 189 @Override 190 public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException 191 { 192 if (path.isEmpty()) 193 { 194 throw new AmetysRepositoryException("path must be non empty"); 195 } 196 197 List<String> headQueuePath = Arrays.asList(StringUtils.split(path, "/", 2)); 198 String name = headQueuePath.get(0); 199 String queuePath = Iterables.get(headQueuePath, 1, null); 200 201 return (A) _traverseCourseLists() 202 .filter(course -> _filterByPageName(course, name)) 203 .findFirst() 204 .map(this::_toCoursePage) 205 .map(cp -> _factory.getODFPageHandler().addRedirectIfNeeded(cp, name)) 206 .map(cp -> _factory.getODFPageHandler().exploreQueuePath(cp, queuePath)) 207 .orElseThrow(() -> new UnknownAmetysObjectException("Unknown child page '" + path + "' for page " + getId())); 208 } 209 210 private boolean _filterByPageName(Course course, String pageName) 211 { 212 // If last part is equals to the course code, the page matches 213 return course.getCode().equals(pageName.substring(pageName.lastIndexOf("-") + 1)); 214 } 215 216 @Override 217 public boolean hasChild(String name) throws AmetysRepositoryException 218 { 219 return _traverseCourseLists() 220 .filter(course -> _filterByPageName(course, name)) 221 .findFirst() 222 .isPresent(); 223 } 224 225 @Override 226 public String getId() throws AmetysRepositoryException 227 { 228 // E.g: course://licence-lea-anglais-allemand-H6YOLDDJ/parcours-1-f7usj1ss?rootId=xxx&courseId=xxx&programId=xxxx 229 return "course://" + _path + "?rootId=" + _root.getId() + "&courseId=" + _course.getId() + "&programId=" + _parentProgram.getId(); 230 } 231 @Override 232 public String getName() throws AmetysRepositoryException 233 { 234 // E.g: langue-anglaise-1-H6YP1P98 235 return _factory.getODFPageHandler().getPageName(_course); 236 } 237 238 @SuppressWarnings("unchecked") 239 @Override 240 public Page getParent() throws AmetysRepositoryException 241 { 242 if (_parentPage == null) 243 { 244 String computeLevelsPath = _factory.getODFPageHandler().computeLevelsPath(_root, _parentProgram); 245 String relParentPath = StringUtils.isNotBlank(computeLevelsPath) ? computeLevelsPath + "/" + _path : _path; 246 _parentPage = _root.getChild(relParentPath); 247 } 248 249 return _parentPage; 250 } 251 252 @Override 253 public String getParentPath() throws AmetysRepositoryException 254 { 255 String computeLevelsPath = _factory.getODFPageHandler().computeLevelsPath(_root, _parentProgram); 256 String relParentPath = StringUtils.isNotBlank(computeLevelsPath) ? computeLevelsPath + "/" + _path : _path; 257 258 return _root.getPath() + "/" + relParentPath; 259 } 260 261 public ModelLessDataHolder getDataHolder() 262 { 263 RepositoryData repositoryData = new MemoryRepositoryData(getName()); 264 return new DefaultModelLessDataHolder(_factory.getPageDataTypeEP(), repositoryData); 265 } 266 267 private Stream<Course> _traverseCourseLists() 268 { 269 CourseListTraverser traverser = new CourseListTraverser(_course.getCourseLists()); 270 return traverser.stream() 271 .filter(Course.class::isInstance) 272 .map(Course.class::cast); 273 } 274 275 static class CourseListTraverser extends AbstractTreeIterator<ProgramItem> 276 { 277 public CourseListTraverser(Collection<? extends ProgramItem> programPartChildren) 278 { 279 super(programPartChildren); 280 } 281 282 @Override 283 protected Iterator<ProgramItem> provideChildIterator(ProgramItem parent) 284 { 285 if (parent instanceof CourseList courseList) 286 { 287 return new CourseListTraverser(courseList.getCourses()); 288 } 289 290 return null; 291 } 292 } 293 294 public ModelAwareDataHolder getTemplateParametersHolder() throws AmetysRepositoryException 295 { 296 return null; 297 } 298}