001/* 002 * Copyright 2011 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.HashSet; 021import java.util.List; 022import java.util.Set; 023 024import org.apache.avalon.framework.component.Component; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.avalon.framework.service.Serviceable; 028import org.apache.commons.lang.StringUtils; 029 030import org.ametys.cms.repository.Content; 031import org.ametys.odf.ODFHelper; 032import org.ametys.odf.ProgramItem; 033import org.ametys.odf.course.Course; 034import org.ametys.odf.courselist.CourseList; 035import org.ametys.odf.program.AbstractProgram; 036import org.ametys.odf.program.Program; 037import org.ametys.odf.program.ProgramPart; 038import org.ametys.odf.program.SubProgram; 039import org.ametys.plugins.repository.AmetysObjectResolver; 040import org.ametys.plugins.repository.UnknownAmetysObjectException; 041import org.ametys.runtime.config.Config; 042import org.ametys.runtime.plugin.component.AbstractLogEnabled; 043import org.ametys.web.repository.page.Page; 044 045/** 046 * Resolves an ODF page path from the associated ODF content. 047 */ 048public class OdfPageResolver extends AbstractLogEnabled implements Component, Serviceable 049{ 050 /** The avalon role. */ 051 public static final String ROLE = OdfPageResolver.class.getName(); 052 053 /** The ametys object resolver. */ 054 protected AmetysObjectResolver _ametysResolver; 055 /** The odf page handler */ 056 protected OdfPageHandler _odfPageHandler; 057 /** ODF helper */ 058 protected ODFHelper _odfHelper; 059 060 @Override 061 public void service(ServiceManager serviceManager) throws ServiceException 062 { 063 _ametysResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 064 _odfPageHandler = (OdfPageHandler) serviceManager.lookup(OdfPageHandler.ROLE); 065 _odfHelper = (ODFHelper) serviceManager.lookup(ODFHelper.ROLE); 066 } 067 068 /** 069 * Get all referencing pages for this program item, in all sites and all sitemaps 070 * @param programItem The program item 071 * @return the referencing pages 072 */ 073 public Set<Page> getReferencingPages(ProgramItem programItem) 074 { 075 return getReferencingPages(programItem, null, ((Content) programItem).getLanguage()); 076 } 077 078 /** 079 * Get all referencing pages for this program item 080 * @param programItem The program item 081 * @param siteName The site name. Can be null to search on all sites 082 * @param lang The sitemap language. Can be null to search on all sitemaps 083 * @return the referencing pages 084 */ 085 public Set<Page> getReferencingPages(ProgramItem programItem, String siteName, String lang) 086 { 087 Set<Page> refPages = new HashSet<>(); 088 089 Set<Page> odfRootPages = _odfPageHandler.getOdfRootPages(siteName, lang); 090 091 for (Page rootPage : odfRootPages) 092 { 093 if (programItem instanceof Program) 094 { 095 ProgramPage programPage = getProgramPage(rootPage, (Program) programItem); 096 if (programPage != null) 097 { 098 refPages.add(programPage); 099 } 100 } 101 else if (programItem instanceof SubProgram) 102 { 103 Set<Program> parentPrograms = programItem.getRootPrograms(); 104 for (Program parentProgram : parentPrograms) 105 { 106 ProgramPage subProgramPage = getSubProgramPage(rootPage, (SubProgram) programItem, parentProgram); 107 if (subProgramPage != null) 108 { 109 refPages.add(subProgramPage); 110 } 111 } 112 } 113 else if (programItem instanceof Course) 114 { 115 List<CourseList> parentCourseLists = ((Course) programItem).getParentCourseLists(); 116 for (CourseList courseList : parentCourseLists) 117 { 118 List<Course> parentCourses = courseList.getParentCourses(); 119 for (Course parentCourse : parentCourses) 120 { 121 CoursePage coursePage = getCoursePage(rootPage, (Course) programItem, parentCourse); 122 if (coursePage != null) 123 { 124 refPages.add(coursePage); 125 } 126 } 127 128 List<AbstractProgram> parentAbstractPrograms = getNearestAncestorAbstractPrograms(courseList); 129 for (AbstractProgram parentAbstractProgram : parentAbstractPrograms) 130 { 131 CoursePage coursePage = getCoursePage(rootPage, (Course) programItem, parentAbstractProgram); 132 if (coursePage != null) 133 { 134 refPages.add(coursePage); 135 } 136 } 137 } 138 } 139 } 140 141 return refPages; 142 } 143 144 /** 145 * Return the program page 146 * @param program the program 147 * @return the page program or null 148 */ 149 public ProgramPage getProgramPage(Program program) 150 { 151 return getProgramPage(program, null); 152 } 153 154 /** 155 * Return the program page 156 * @param program the program 157 * @param siteName The current site name. If the no ODF root page is present in this site, the default ODF site will be used instead. 158 * @return the page program or null 159 */ 160 public ProgramPage getProgramPage(Program program, String siteName) 161 { 162 Page odfRootPage = getOdfRootPage(siteName, program.getLanguage(), program.getCatalog()); 163 164 if (odfRootPage == null) 165 { 166 return null; 167 } 168 169 return getProgramPage(odfRootPage, program); 170 } 171 172 /** 173 * Return the program page 174 * @param odfRootPage the odf root page 175 * @param program the program 176 * @return the page program or null 177 */ 178 public ProgramPage getProgramPage (Page odfRootPage, Program program) 179 { 180 // E.g: program://_root?rootId=xxxx&programId=xxxx 181 String pageId = "program://_root?rootId=" + odfRootPage.getId() + "&programId=" + program.getId(); 182 try 183 { 184 return _ametysResolver.resolveById(pageId); 185 } 186 catch (UnknownAmetysObjectException e) 187 { 188 return null; 189 } 190 } 191 192 /** 193 * Return the subprogram page 194 * @param subProgram the subprogram 195 * @param parentProgram The parent program 196 * @param siteName The current site name. If the no ODF root page is present in this site, the default ODF site will be used instead. 197 * @return the subprogram page or null 198 */ 199 public ProgramPage getSubProgramPage(SubProgram subProgram, AbstractProgram parentProgram, String siteName) 200 { 201 Page odfRootPage = getOdfRootPage(siteName, subProgram.getLanguage(), subProgram.getCatalog()); 202 203 if (odfRootPage == null) 204 { 205 return null; 206 } 207 208 return getSubProgramPage(odfRootPage, subProgram, parentProgram); 209 } 210 211 /** 212 * Return the subprogram page 213 * @param odfRootPage the odf root page 214 * @param subProgram the subprogram 215 * @param parentAbstractProgram The parent program or subprogram 216 * @return the subprogram page or null 217 */ 218 public ProgramPage getSubProgramPage (Page odfRootPage, SubProgram subProgram, AbstractProgram parentAbstractProgram) 219 { 220 AbstractProgram parent = getNearestAncestorAbstractProgram(subProgram, parentAbstractProgram); 221 Program parentProgram = getParentProgram(subProgram, parentAbstractProgram); 222 223 if (parent == null || parentProgram == null) 224 { 225 // No page 226 return null; 227 } 228 229 // Id is like program://path/to/subprogram?rootId=xxxx&programId=xxxx&parentId=xxxx 230 String pageId = "program://" + getPathInProgram(parent, parentProgram) + "?rootId=" + odfRootPage.getId() + "&programId=" + subProgram.getId() + "&parentId=" + parentProgram.getId(); 231 try 232 { 233 return _ametysResolver.resolveById(pageId); 234 } 235 catch (UnknownAmetysObjectException e) 236 { 237 return null; 238 } 239 } 240 241 /** 242 * Return the course page 243 * @param course the course 244 * @param parentProgram the parent program or subprogram. Can be null. 245 * @param siteName The current site name. If the no ODF root page is present in this site, the default ODF site will be used instead. 246 * @return the course page or null if not found 247 */ 248 public CoursePage getCoursePage(Course course, AbstractProgram parentProgram, String siteName) 249 { 250 String catalog = course.getCatalog(); 251 Page odfRootPage = getOdfRootPage(siteName, course.getLanguage(), catalog); 252 253 if (odfRootPage == null) 254 { 255 return null; 256 } 257 258 return getCoursePage(odfRootPage, course, parentProgram); 259 } 260 261 /** 262 * Return the course page 263 * @param odfRootPage the odf root page 264 * @param course the course 265 * @param parentAbstractProgram the parent program or subprogram. Can be null. 266 * @return the course page or null if not found 267 */ 268 public CoursePage getCoursePage (Page odfRootPage, Course course, AbstractProgram parentAbstractProgram) 269 { 270 AbstractProgram parent = getNearestAncestorAbstractProgram(course, parentAbstractProgram); 271 Program parentProgram = getParentProgram(course, parentAbstractProgram); 272 273 if (parent == null || parentProgram == null) 274 { 275 // No page 276 return null; 277 } 278 279 // Id is like course://path/from/program?rootId=xxx&courseId=xxx&programId=xxxx 280 String pageId = "course://" + getPathInProgram(parent, parentProgram) + "?rootId=" + odfRootPage.getId() + "&courseId=" + course.getId() + "&programId=" + parentProgram.getId(); 281 try 282 { 283 return _ametysResolver.resolveById(pageId); 284 } 285 catch (UnknownAmetysObjectException e) 286 { 287 return null; 288 } 289 } 290 291 /** 292 * Return the course page 293 * @param course the course 294 * @param parentCourse the parent course. Can NOT be null. 295 * @param siteName The current site name. If the no ODF root page is present in this site, the default ODF site will be used instead. 296 * @return the course page or null if not found 297 */ 298 public CoursePage getCoursePage (Course course, Course parentCourse, String siteName) 299 { 300 String catalog = course.getCatalog(); 301 Page odfRootPage = getOdfRootPage(siteName, course.getLanguage(), catalog); 302 303 if (odfRootPage == null) 304 { 305 return null; 306 } 307 308 return getCoursePage(odfRootPage, course, parentCourse); 309 } 310 311 /** 312 * Return the course page 313 * @param odfRootPage the odf root page 314 * @param course the course 315 * @param parentCourse the parent course. Can NOT be null. 316 * @return the course page or null if not found 317 */ 318 public CoursePage getCoursePage (Page odfRootPage, Course course, Course parentCourse) 319 { 320 AbstractProgram parent = getNearestAncestorAbstractProgram(parentCourse, null); 321 Program parentProgram = getParentProgram(parentCourse, null); 322 323 if (parent == null || parentProgram == null) 324 { 325 // No page 326 return null; 327 } 328 329 // Id is like course://path/from/program?rootId=xxx&courseId=xxx&programId=xxxx 330 String pageId = "course://" + getPathInProgram(parentCourse, parentProgram) + "?rootId=" + odfRootPage.getId() + "&courseId=" + course.getId() + "&programId=" + parentProgram.getId(); 331 try 332 { 333 return _ametysResolver.resolveById(pageId); 334 } 335 catch (UnknownAmetysObjectException e) 336 { 337 return null; 338 } 339 } 340 341 /** 342 * Get the ODF root page, either in the given site if it exists, or in the default ODF site. 343 * @param siteName the desired site name. 344 * @param language the sitemap language to search in. 345 * @param catalog The ODF catalog 346 * @return the ODF root page, either in the given site if it exists, or in the default ODF site. 347 */ 348 public Page getOdfRootPage(String siteName, String language, String catalog) 349 { 350 Page odfRootPage = null; 351 352 if (StringUtils.isNotEmpty(siteName)) 353 { 354 odfRootPage = _odfPageHandler.getOdfRootPage(siteName, language, catalog); 355 } 356 357 if (odfRootPage == null) 358 { 359 String odfSiteName = Config.getInstance().getValue("odf.web.site.name"); 360 odfRootPage = _odfPageHandler.getOdfRootPage(odfSiteName, language, catalog); 361 } 362 363 return odfRootPage; 364 } 365 366 /** 367 * Get the path in sitemap of a ODF content into a {@link Program} or {@link SubProgram}<br> 368 * Be careful, this is the path in sitemap, to get the path of a item into a Program, use {@link ODFHelper#getPathInProgram} instead. 369 * @param item The program item 370 * @param parentProgram The parent root (sub)program. Can be null. 371 * @return the path in sitemap from the parent program 372 */ 373 public String getPathInProgram (ProgramItem item, AbstractProgram parentProgram) 374 { 375 if (item instanceof Program || item.equals(parentProgram)) 376 { 377 // The program item is already the program it self 378 return _odfPageHandler.getPageName(item); 379 } 380 381 List<String> paths = new ArrayList<>(); 382 if (item instanceof AbstractProgram || item instanceof Course) 383 { 384 paths.add(_odfPageHandler.getPageName(item)); 385 } 386 387 ProgramItem parent = _odfHelper.getParentProgramItem(item, parentProgram); 388 while (parent != null && !(parent instanceof Program)) 389 { 390 if (parent instanceof AbstractProgram || parent instanceof Course) 391 { 392 paths.add(_odfPageHandler.getPageName(parent)); 393 } 394 parent = _odfHelper.getParentProgramItem(parent, parentProgram); 395 } 396 397 if (parent != null) 398 { 399 paths.add(_odfPageHandler.getPageName(parent)); 400 Collections.reverse(paths); 401 return org.apache.commons.lang3.StringUtils.join(paths, "/"); 402 } 403 404 return null; 405 } 406 407 /** 408 * Returns the first {@link Program} ancestor, ensuring that the given parent content 'parentProgram' is in the hierarchy, if not null.<br> 409 * If 'parentProgram' is null, the first {@link Program} ancestor will be returned regardless of parent hierarchy.<br> 410 * If 'parentProgram' is a {@link SubProgram}, the first {@link Program} ancestor from this {@link SubProgram} will be returned regardless of parent hierarchy 411 * @param programItem a {@link ProgramItem} 412 * @param parentProgram The parent program or subprogram. Can be null. 413 * @return the parent {@link Program} into this (sub)program or null if not found 414 */ 415 public Program getParentProgram (ProgramItem programItem, AbstractProgram parentProgram) 416 { 417 AbstractProgram parent = parentProgram; 418 419 ProgramItem parentItem = _odfHelper.getParentProgramItem(programItem, parentProgram); 420 while (parentItem != null && !(parentItem instanceof Program)) 421 { 422 if (parent != null && parentItem.equals(parent)) 423 { 424 // Once the desired abstract program parent is passed, the parent is null 425 parent = null; 426 } 427 parentItem = _odfHelper.getParentProgramItem(parentItem, parent); 428 } 429 430 return parentItem != null ? (Program) parentItem : null; 431 } 432 433 /** 434 * Returns the nearest {@link AbstractProgram} ancestors. 435 * @param programPart a {@link ProgramPart} 436 * @return the nearest {@link AbstractProgram} ancestors containing this program part 437 */ 438 public List<AbstractProgram> getNearestAncestorAbstractPrograms (ProgramPart programPart) 439 { 440 List<AbstractProgram> ancestors = new ArrayList<>(); 441 442 List<ProgramPart> parents = programPart.getProgramPartParents(); 443 for (ProgramPart parent : parents) 444 { 445 if (parent instanceof AbstractProgram) 446 { 447 ancestors.add((AbstractProgram) parent); 448 } 449 else 450 { 451 ancestors.addAll(getNearestAncestorAbstractPrograms(parent)); 452 } 453 } 454 455 return ancestors; 456 } 457 458 /** 459 * Returns the nearest {@link AbstractProgram} ancestor. 460 * @param programItem a {@link ProgramItem} 461 * @param parentProgram The parent program or subprogram 462 * @return the nearest {@link AbstractProgram} ancestor into this (sub)program or null if not found 463 */ 464 public AbstractProgram getNearestAncestorAbstractProgram (ProgramItem programItem, AbstractProgram parentProgram) 465 { 466 ProgramItem parentItem = _odfHelper.getParentProgramItem(programItem, parentProgram); 467 while (parentItem != null && !(parentItem instanceof AbstractProgram)) 468 { 469 parentItem = _odfHelper.getParentProgramItem(parentItem, parentProgram); 470 } 471 472 return parentItem != null ? (AbstractProgram) parentItem : null; 473 } 474 475 /** 476 * Get the path of a Program page from the Program content. 477 * @param siteName the site name. 478 * @param language the language. 479 * @param program the program. 480 * @return the page path or empty if no page matches 481 */ 482 public String getProgramPagePath(String siteName, String language, Program program) 483 { 484 return getProgramItemPagePath(siteName, language, program, program); 485 } 486 487 /** 488 * Get the path of a {@link ProgramItem} page into the given {@link Program} 489 * @param siteName the site name 490 * @param language the language 491 * @param programItem the subprogram. 492 * @param parentProgram The parent program 493 * @return the page path or empty if no page matches 494 */ 495 public String getProgramItemPagePath(String siteName, String language, ProgramItem programItem, Program parentProgram) 496 { 497 StringBuilder sb = new StringBuilder(); 498 499 Page rootPage = _odfPageHandler.getOdfRootPage(siteName, language, programItem.getCatalog()); 500 if (rootPage != null) 501 { 502 if (programItem instanceof Program) 503 { 504 sb.append(rootPage.getSitemapName()).append('/') 505 .append(rootPage.getPathInSitemap()).append('/') 506 .append(_odfPageHandler.getLevel1PageName(rootPage, parentProgram)).append('/') 507 .append(_odfPageHandler.getLevel2PageName(rootPage, parentProgram)).append('/') 508 .append(_odfPageHandler.getPageName(programItem)); 509 } 510 else 511 { 512 String pathInProgram = getPathInProgram(programItem, parentProgram); 513 if (pathInProgram != null) 514 { 515 sb.append(rootPage.getSitemapName()).append('/') 516 .append(rootPage.getPathInSitemap()).append('/') 517 .append(_odfPageHandler.getLevel1PageName(rootPage, parentProgram)).append('/') 518 .append(_odfPageHandler.getLevel2PageName(rootPage, parentProgram)).append('/') 519 .append(pathInProgram); 520 } 521 } 522 523 } 524 525 return sb.toString(); 526 } 527}