001/* 002 * Copyright 2024 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.Collections; 020import java.util.List; 021import java.util.NoSuchElementException; 022import java.util.stream.Collectors; 023 024import org.ametys.odf.ProgramItem; 025import org.ametys.odf.course.Course; 026import org.ametys.odf.data.EducationalPath; 027import org.ametys.odf.program.AbstractProgram; 028import org.ametys.odf.program.Program; 029import org.ametys.plugins.repository.AmetysObject; 030import org.ametys.plugins.repository.AmetysObjectIterable; 031import org.ametys.plugins.repository.AmetysObjectIterator; 032import org.ametys.plugins.repository.AmetysRepositoryException; 033import org.ametys.plugins.repository.UnknownAmetysObjectException; 034import org.ametys.web.repository.page.Page; 035import org.ametys.web.repository.page.virtual.VirtualPageConfiguration; 036 037/** 038 * Common class for ODF virtual page representing a {@link ProgramItem} 039 * @param <T> The type of factory to use 040 */ 041public abstract class AbstractProgramItemPage<T extends AbstractOdfPageFactory> extends AbstractOdfPage<T> 042{ 043 /** 044 * The constructor of abstract program item pages 045 * @param root the odf root page. 046 * @param configuration The abstract virtual page configuration 047 * @param scheme The scheme 048 * @param factory The factory 049 */ 050 public AbstractProgramItemPage(Page root, VirtualPageConfiguration configuration, String scheme, T factory) 051 { 052 super(root, configuration, scheme, factory); 053 } 054 055 @SuppressWarnings("unchecked") 056 @Override 057 public AmetysObjectIterable<? extends AmetysObject> getChildren() throws AmetysRepositoryException 058 { 059 return getChildrenPages(); 060 } 061 062 @Override 063 public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePages) throws AmetysRepositoryException 064 { 065 return getChildrenPages(); 066 } 067 068 @Override 069 public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException 070 { 071 if (index < 0) 072 { 073 throw new AmetysRepositoryException("Child page index cannot be negative"); 074 } 075 076 AmetysObjectIterator<? extends Page> childPages = getChildrenPages().iterator(); 077 078 try 079 { 080 childPages.skip(index); 081 return childPages.next(); 082 } 083 catch (NoSuchElementException e) 084 { 085 throw new UnknownAmetysObjectException("There's no child page at index " + index + " for page " + this.getId()); 086 } 087 } 088 089 /** 090 * Get the program item wrapped in this page 091 * @return the program item 092 */ 093 protected abstract ProgramItem getProgramItem(); 094 095 /** 096 * Get the ODF path corresponding to this proram item from the root {@link Program} 097 * The ODF path is composed with the ids of program, subprogram and course parents of each child pages until this page 098 * @return the ODF path from the root {@link Program} 099 */ 100 public List<ProgramItem> getOdfPath() 101 { 102 List<ProgramItem> path = new ArrayList<>(); 103 path.add(getProgramItem()); 104 105 Page parentPage = getParent(); 106 107 while (parentPage instanceof AbstractProgramItemPage programItemPage) 108 { 109 path.add(programItemPage.getProgramItem()); 110 parentPage = parentPage.getParent(); 111 } 112 113 Collections.reverse(path); 114 return path; 115 } 116 117 /** 118 * Set the current educational paths of the wrapped program item 119 * @param programItem the wrapped program item 120 */ 121 protected void setCurrentEducationalPaths(ProgramItem programItem) 122 { 123 // Ignore education path 124 List<EducationalPath> educationalPaths = _factory._odfHelper.getEducationalPaths(programItem, true, true); 125 126 List<EducationalPath> currentEducationalPaths = educationalPaths.stream() 127 .filter(this::_isPartOfCurrentPath) 128 .collect(Collectors.toList()); 129 130 if (!currentEducationalPaths.isEmpty()) 131 { 132 if (programItem instanceof Course course) 133 { 134 course.setCurrentEducationalPaths(currentEducationalPaths); 135 } 136 else if (programItem instanceof AbstractProgram abstractProgram) 137 { 138 abstractProgram.setCurrentEducationalPaths(currentEducationalPaths); 139 } 140 } 141 142 } 143 144 private boolean _isPartOfCurrentPath(EducationalPath path) 145 { 146 List<ProgramItem> currentOdfPathInSitemap = getOdfPath(); 147 148 // Filter on program items that is part of ODF pages 149 List<ProgramItem> filteredProgramItemIds = path.getProgramItems(_factory.getResolver()) 150 .stream() 151 .filter(pi -> pi instanceof AbstractProgram || pi instanceof Course) 152 .toList(); 153 154 return currentOdfPathInSitemap.equals(filteredProgramItemIds); 155 } 156 157}