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 = _odfHelper.getParentPrograms(programItem); 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 ProgramItem parent = null; 271 272 Course parentCourse = getNearestAncestorCourse(course, parentAbstractProgram); 273 if (parentCourse != null) 274 { 275 parent = parentCourse; 276 } 277 else 278 { 279 parent = getNearestAncestorAbstractProgram(course, parentAbstractProgram); 280 } 281 Program parentProgram = getParentProgram(course, parentAbstractProgram); 282 283 if (parent == null || parentProgram == null) 284 { 285 // No page 286 return null; 287 } 288 289 // Test program restriction 290 if (!_odfPageHandler.isValidRestriction(odfRootPage, parentProgram)) 291 { 292 return null; 293 } 294 295 // Id is like course://path/from/program?rootId=xxx&courseId=xxx&programId=xxxx 296 String pageId = "course://" + getPathInProgram(parent, parentProgram) + "?rootId=" + odfRootPage.getId() + "&courseId=" + course.getId() + "&programId=" + parentProgram.getId(); 297 try 298 { 299 return _ametysResolver.resolveById(pageId); 300 } 301 catch (UnknownAmetysObjectException e) 302 { 303 return null; 304 } 305 } 306 307 /** 308 * Return the course page 309 * @param course the course 310 * @param parentCourse the parent course. Can NOT be null. 311 * @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. 312 * @return the course page or null if not found 313 */ 314 public CoursePage getCoursePage (Course course, Course parentCourse, String siteName) 315 { 316 String catalog = course.getCatalog(); 317 Page odfRootPage = getOdfRootPage(siteName, course.getLanguage(), catalog); 318 319 if (odfRootPage == null) 320 { 321 return null; 322 } 323 324 return getCoursePage(odfRootPage, course, parentCourse); 325 } 326 327 /** 328 * Return the course page 329 * @param odfRootPage the odf root page 330 * @param course the course 331 * @param parentCourse the parent course. Can NOT be null. 332 * @return the course page or null if not found 333 */ 334 public CoursePage getCoursePage (Page odfRootPage, Course course, Course parentCourse) 335 { 336 AbstractProgram parent = getNearestAncestorAbstractProgram(parentCourse, null); 337 Program parentProgram = getParentProgram(parentCourse, null); 338 339 if (parent == null || parentProgram == null) 340 { 341 // No page 342 return null; 343 } 344 345 // Test program restriction 346 if (!_odfPageHandler.isValidRestriction(odfRootPage, parentProgram)) 347 { 348 return null; 349 } 350 351 // Id is like course://path/from/program?rootId=xxx&courseId=xxx&programId=xxxx 352 String pageId = "course://" + getPathInProgram(parentCourse, parentProgram) + "?rootId=" + odfRootPage.getId() + "&courseId=" + course.getId() + "&programId=" + parentProgram.getId(); 353 try 354 { 355 return _ametysResolver.resolveById(pageId); 356 } 357 catch (UnknownAmetysObjectException e) 358 { 359 return null; 360 } 361 } 362 363 /** 364 * Get the ODF root page, either in the given site if it exists, or in the default ODF site. 365 * @param siteName the desired site name. 366 * @param language the sitemap language to search in. 367 * @param catalog The ODF catalog 368 * @return the ODF root page, either in the given site if it exists, or in the default ODF site. 369 */ 370 public Page getOdfRootPage(String siteName, String language, String catalog) 371 { 372 Page odfRootPage = null; 373 374 if (StringUtils.isNotEmpty(siteName)) 375 { 376 odfRootPage = _odfPageHandler.getOdfRootPage(siteName, language, catalog); 377 } 378 379 if (odfRootPage == null) 380 { 381 String odfSiteName = Config.getInstance().getValue("odf.web.site.name"); 382 odfRootPage = _odfPageHandler.getOdfRootPage(odfSiteName, language, catalog); 383 } 384 385 return odfRootPage; 386 } 387 388 /** 389 * Get the path in sitemap of a ODF content into a {@link Program} or {@link SubProgram}<br> 390 * Be careful, this is the path in sitemap, to get the path of a item into a Program, use {@link ODFHelper#getPathInProgram} instead. 391 * @param item The program item 392 * @param parentProgram The parent root (sub)program. Can be null. 393 * @return the path in sitemap from the parent program 394 */ 395 public String getPathInProgram (ProgramItem item, AbstractProgram parentProgram) 396 { 397 if (item instanceof Program || item.equals(parentProgram)) 398 { 399 // The program item is already the program it self 400 return _odfPageHandler.getPageName(item); 401 } 402 403 List<String> paths = new ArrayList<>(); 404 if (item instanceof AbstractProgram || item instanceof Course) 405 { 406 paths.add(_odfPageHandler.getPageName(item)); 407 } 408 409 ProgramItem parent = _odfHelper.getParentProgramItem(item, parentProgram); 410 while (parent != null && !(parent instanceof Program)) 411 { 412 if (parent instanceof AbstractProgram || parent instanceof Course) 413 { 414 paths.add(_odfPageHandler.getPageName(parent)); 415 } 416 parent = _odfHelper.getParentProgramItem(parent, parentProgram); 417 } 418 419 if (parent != null) 420 { 421 paths.add(_odfPageHandler.getPageName(parent)); 422 Collections.reverse(paths); 423 return org.apache.commons.lang3.StringUtils.join(paths, "/"); 424 } 425 426 return null; 427 } 428 429 /** 430 * Returns the first {@link Program} ancestor, ensuring that the given parent content 'parentProgram' is in the hierarchy, if not null.<br> 431 * If 'parentProgram' is null, the first {@link Program} ancestor will be returned regardless of parent hierarchy.<br> 432 * If 'parentProgram' is a {@link SubProgram}, the first {@link Program} ancestor from this {@link SubProgram} will be returned regardless of parent hierarchy 433 * @param programItem a {@link ProgramItem} 434 * @param parentProgram The parent program or subprogram. Can be null. 435 * @return the parent {@link Program} into this (sub)program or null if not found 436 */ 437 public Program getParentProgram (ProgramItem programItem, AbstractProgram parentProgram) 438 { 439 AbstractProgram parent = parentProgram; 440 441 ProgramItem parentItem = _odfHelper.getParentProgramItem(programItem, parentProgram); 442 while (parentItem != null && !(parentItem instanceof Program)) 443 { 444 if (parent != null && parentItem.equals(parent)) 445 { 446 // Once the desired abstract program parent is passed, the parent is null 447 parent = null; 448 } 449 parentItem = _odfHelper.getParentProgramItem(parentItem, parent); 450 } 451 452 return parentItem != null ? (Program) parentItem : null; 453 } 454 455 /** 456 * Returns the nearest {@link AbstractProgram} ancestors. 457 * @param programPart a {@link ProgramPart} 458 * @return the nearest {@link AbstractProgram} ancestors containing this program part 459 */ 460 public List<AbstractProgram> getNearestAncestorAbstractPrograms (ProgramPart programPart) 461 { 462 List<AbstractProgram> ancestors = new ArrayList<>(); 463 464 List<ProgramPart> parents = programPart.getProgramPartParents(); 465 for (ProgramPart parent : parents) 466 { 467 if (parent instanceof AbstractProgram) 468 { 469 ancestors.add((AbstractProgram) parent); 470 } 471 else 472 { 473 ancestors.addAll(getNearestAncestorAbstractPrograms(parent)); 474 } 475 } 476 477 return ancestors; 478 } 479 480 /** 481 * Returns the nearest {@link AbstractProgram} ancestor. 482 * @param programItem a {@link ProgramItem} 483 * @param parentProgram The parent program or subprogram 484 * @return the nearest {@link AbstractProgram} ancestor into this (sub)program or null if not found 485 */ 486 public AbstractProgram getNearestAncestorAbstractProgram (ProgramItem programItem, AbstractProgram parentProgram) 487 { 488 ProgramItem parentItem = _odfHelper.getParentProgramItem(programItem, parentProgram); 489 while (parentItem != null && !(parentItem instanceof AbstractProgram)) 490 { 491 parentItem = _odfHelper.getParentProgramItem(parentItem, parentProgram); 492 } 493 494 return parentItem != null ? (AbstractProgram) parentItem : null; 495 } 496 497 /** 498 * Returns the nearest {@link Course} ancestor. 499 * @param course a {@link Course} 500 * @param parentProgram The parent program or subprogram 501 * @return the nearest {@link Course} ancestor into this (sub)program or null if not found 502 */ 503 public Course getNearestAncestorCourse (Course course, AbstractProgram parentProgram) 504 { 505 ProgramItem parentItem = _odfHelper.getParentProgramItem(course, parentProgram); 506 while (parentItem != null && !(parentItem instanceof Course) && !(parentItem instanceof AbstractProgram)) 507 { 508 parentItem = _odfHelper.getParentProgramItem(parentItem, parentProgram); 509 } 510 511 return parentItem != null && parentItem instanceof Course ? (Course) parentItem : null; 512 } 513 514 /** 515 * Get the path of a Program page from the Program content. 516 * @param siteName the site name. 517 * @param language the language. 518 * @param program the program. 519 * @return the page path or empty if no page matches 520 */ 521 public String getProgramPagePath(String siteName, String language, Program program) 522 { 523 return getProgramItemPagePath(siteName, language, program, program); 524 } 525 526 /** 527 * Get the path of a {@link ProgramItem} page into the given {@link Program} 528 * @param siteName the site name 529 * @param language the language 530 * @param programItem the subprogram. 531 * @param parentProgram The parent program 532 * @return the page path or empty if no page matches 533 */ 534 public String getProgramItemPagePath(String siteName, String language, ProgramItem programItem, Program parentProgram) 535 { 536 StringBuilder sb = new StringBuilder(); 537 538 Page rootPage = _odfPageHandler.getOdfRootPage(siteName, language, programItem.getCatalog()); 539 if (rootPage != null) 540 { 541 if (programItem instanceof Program) 542 { 543 sb.append(rootPage.getSitemapName()).append('/') 544 .append(rootPage.getPathInSitemap()).append('/') 545 .append(_odfPageHandler.getLevel1PageName(rootPage, parentProgram)).append('/') 546 .append(_odfPageHandler.getLevel2PageName(rootPage, parentProgram)).append('/') 547 .append(_odfPageHandler.getPageName(programItem)); 548 } 549 else 550 { 551 String pathInProgram = getPathInProgram(programItem, parentProgram); 552 if (pathInProgram != null) 553 { 554 sb.append(rootPage.getSitemapName()).append('/') 555 .append(rootPage.getPathInSitemap()).append('/') 556 .append(_odfPageHandler.getLevel1PageName(rootPage, parentProgram)).append('/') 557 .append(_odfPageHandler.getLevel2PageName(rootPage, parentProgram)).append('/') 558 .append(pathInProgram); 559 } 560 } 561 562 } 563 564 return sb.toString(); 565 } 566}