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