001/* 002 * Copyright 2019 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.odf.content; 017 018import java.io.IOException; 019import java.util.List; 020import java.util.Locale; 021import java.util.Optional; 022import java.util.Set; 023 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.cocoon.ProcessingException; 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.environment.Request; 029import org.apache.cocoon.generation.ServiceableGenerator; 030import org.apache.cocoon.xml.AttributesImpl; 031import org.apache.cocoon.xml.XMLUtils; 032import org.apache.commons.lang.StringUtils; 033import org.xml.sax.SAXException; 034 035import org.ametys.cms.contenttype.ContentTypesHelper; 036import org.ametys.cms.repository.Content; 037import org.ametys.odf.NoLiveVersionException; 038import org.ametys.odf.ODFHelper; 039import org.ametys.odf.ProgramItem; 040import org.ametys.odf.course.Course; 041import org.ametys.odf.courselist.CourseList; 042import org.ametys.odf.coursepart.CoursePart; 043import org.ametys.odf.enumeration.OdfReferenceTableEntry; 044import org.ametys.odf.enumeration.OdfReferenceTableHelper; 045import org.ametys.odf.program.AbstractProgram; 046import org.ametys.odf.program.Container; 047import org.ametys.odf.program.Program; 048import org.ametys.odf.program.SubProgram; 049import org.ametys.plugins.repository.AmetysRepositoryException; 050import org.ametys.plugins.repository.jcr.DefaultAmetysObject; 051import org.ametys.runtime.model.View; 052import org.ametys.runtime.model.exception.BadItemTypeException; 053import org.ametys.runtime.model.type.DataContext; 054 055/** 056 * SAX the structure (ie. the child program items) of a {@link ProgramItem} 057 * 058 */ 059public class ProgramItemStructureGenerator extends ServiceableGenerator 060{ 061 private static final Set<String> __ALLOWED_VIEW_NAMES = Set.of("main", "pdf"); 062 063 /** The ODF helper */ 064 protected ODFHelper _odfHelper; 065 /** Helper for ODF reference table */ 066 protected OdfReferenceTableHelper _odfReferenceTableHelper; 067 /** The content types helper */ 068 protected ContentTypesHelper _cTypesHelper; 069 070 @Override 071 public void service(ServiceManager smanager) throws ServiceException 072 { 073 _odfHelper = (ODFHelper) smanager.lookup(ODFHelper.ROLE); 074 _odfReferenceTableHelper = (OdfReferenceTableHelper) smanager.lookup(OdfReferenceTableHelper.ROLE); 075 _cTypesHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE); 076 } 077 078 public void generate() throws IOException, SAXException, ProcessingException 079 { 080 Request request = ObjectModelHelper.getRequest(objectModel); 081 Content content = (Content) request.getAttribute(Content.class.getName()); 082 083 String viewName = parameters.getParameter("viewName", StringUtils.EMPTY); 084 String fallbackViewName = parameters.getParameter("fallbackViewName", StringUtils.EMPTY); 085 086 View view = _cTypesHelper.getViewWithFallback(viewName, fallbackViewName, content); 087 088 contentHandler.startDocument(); 089 090 if (view != null && __ALLOWED_VIEW_NAMES.contains(view.getName())) 091 { 092 if (content instanceof ProgramItem) 093 { 094 XMLUtils.startElement(contentHandler, "structure"); 095 saxProgramItem((ProgramItem) content); 096 XMLUtils.endElement(contentHandler, "structure"); 097 } 098 else 099 { 100 getLogger().warn("Cannot get the structure of a non program item '" + content.getId() + "'"); 101 } 102 } 103 104 contentHandler.endDocument(); 105 } 106 107 /** 108 * SAX a program item with its child program items 109 * @param programItem the program item 110 * @throws SAXException if an error occurs while saxing 111 */ 112 protected void saxProgramItem(ProgramItem programItem) throws SAXException 113 { 114 if (programItem instanceof Program) 115 { 116 saxProgram((Program) programItem); 117 } 118 else if (programItem instanceof SubProgram) 119 { 120 saxSubProgram((SubProgram) programItem); 121 } 122 else if (programItem instanceof Container) 123 { 124 saxContainer((Container) programItem); 125 } 126 else if (programItem instanceof CourseList) 127 { 128 saxCourseList((CourseList) programItem); 129 } 130 else if (programItem instanceof Course) 131 { 132 saxCourse((Course) programItem); 133 } 134 } 135 136 /** 137 * SAX the child program items 138 * @param programItem the program item 139 * @throws SAXException if an error occurs while saxing 140 */ 141 protected void saxChildProgramItems(ProgramItem programItem) throws SAXException 142 { 143 List<ProgramItem> childProgramItems = _odfHelper.getChildProgramItems(programItem); 144 for (ProgramItem childProgramItem : childProgramItems) 145 { 146 try 147 { 148 _odfHelper.switchToLiveVersionIfNeeded((DefaultAmetysObject) childProgramItem); 149 saxProgramItem(childProgramItem); 150 } 151 catch (NoLiveVersionException e) 152 { 153 // Just ignore the program item 154 } 155 } 156 } 157 158 /** 159 * SAX a program 160 * @param program the subprogram to SAX 161 * @throws SAXException if an error occurs 162 */ 163 protected void saxProgram(Program program) throws SAXException 164 { 165 AttributesImpl attrs = new AttributesImpl(); 166 _saxCommonAttributes(program, attrs); 167 168 XMLUtils.startElement(contentHandler, "program", attrs); 169 170 XMLUtils.startElement(contentHandler, "attributes"); 171 _saxStructureViewIfExists(program); 172 XMLUtils.endElement(contentHandler, "attributes"); 173 174 saxChildProgramItems(program); 175 XMLUtils.endElement(contentHandler, "program"); 176 } 177 178 /** 179 * SAX a subprogram 180 * @param subProgram the subprogram to SAX 181 * @throws SAXException if an error occurs 182 */ 183 protected void saxSubProgram(SubProgram subProgram) throws SAXException 184 { 185 AttributesImpl attrs = new AttributesImpl(); 186 _saxCommonAttributes(subProgram, attrs); 187 188 XMLUtils.startElement(contentHandler, "subprogram", attrs); 189 190 XMLUtils.startElement(contentHandler, "attributes"); 191 _saxReferenceTableItem(subProgram.getEcts(), AbstractProgram.ECTS, subProgram.getLanguage()); 192 _saxStructureViewIfExists(subProgram); 193 XMLUtils.endElement(contentHandler, "attributes"); 194 195 saxChildProgramItems(subProgram); 196 197 XMLUtils.endElement(contentHandler, "subprogram"); 198 } 199 200 /** 201 * SAX a container 202 * @param container the container to SAX 203 * @throws SAXException if an error occurs while saxing 204 */ 205 protected void saxContainer(Container container) throws SAXException 206 { 207 AttributesImpl attrs = new AttributesImpl(); 208 _saxCommonAttributes(container, attrs); 209 210 XMLUtils.startElement(contentHandler, "container", attrs); 211 212 XMLUtils.startElement(contentHandler, "attributes"); 213 _saxReferenceTableItem(container.getNature(), Container.NATURE, container.getLanguage()); 214 _saxReferenceTableItem(container.getPeriod(), Container.PERIOD, container.getLanguage()); 215 216 _saxStructureViewIfExists(container); 217 218 XMLUtils.endElement(contentHandler, "attributes"); 219 220 saxChildProgramItems(container); 221 222 XMLUtils.endElement(contentHandler, "container"); 223 } 224 225 /** 226 * SAX a course list 227 * @param cl the course list to SAX 228 * @throws SAXException if an error occurs while saxing 229 */ 230 protected void saxCourseList(CourseList cl) throws SAXException 231 { 232 AttributesImpl attrs = new AttributesImpl(); 233 _saxCommonAttributes(cl, attrs); 234 235 XMLUtils.startElement(contentHandler, "courselist", attrs); 236 237 XMLUtils.startElement(contentHandler, "attributes"); 238 _saxStructureViewIfExists(cl); 239 XMLUtils.endElement(contentHandler, "attributes"); 240 241 saxChildProgramItems(cl); 242 243 XMLUtils.endElement(contentHandler, "courselist"); 244 } 245 246 /** 247 * SAX a course 248 * @param course the container to SAX 249 * @throws SAXException if an error occurs while saxing 250 */ 251 protected void saxCourse(Course course) throws SAXException 252 { 253 AttributesImpl attrs = new AttributesImpl(); 254 _saxCommonAttributes(course, attrs); 255 256 XMLUtils.startElement(contentHandler, "course", attrs); 257 258 XMLUtils.startElement(contentHandler, "attributes"); 259 _saxReferenceTableItem(course.getCourseType(), Course.COURSE_TYPE, course.getLanguage()); 260 _saxStructureViewIfExists(course); 261 XMLUtils.endElement(contentHandler, "attributes"); 262 263 saxChildProgramItems(course); 264 265 saxCourseParts(course); 266 267 XMLUtils.endElement(contentHandler, "course"); 268 } 269 270 /** 271 * SAX a course part 272 * @param course The course 273 * @throws SAXException if an error occurs 274 */ 275 protected void saxCourseParts(Course course) throws SAXException 276 { 277 List<CoursePart> courseParts = course.getCourseParts(); 278 279 double totalHours = 0; 280 for (CoursePart coursePart : course.getCourseParts()) 281 { 282 totalHours += coursePart.getNumberOfHours(); 283 } 284 285 AttributesImpl attrs = new AttributesImpl(); 286 attrs.addCDATAAttribute("totalHours", String.valueOf(totalHours)); 287 XMLUtils.startElement(contentHandler, "courseparts", attrs); 288 289 for (CoursePart coursePart : courseParts) 290 { 291 saxCoursePart(coursePart); 292 } 293 294 XMLUtils.endElement(contentHandler, "courseparts"); 295 } 296 297 /** 298 * SAX a course part 299 * @param coursePart The course part to SAX 300 * @throws SAXException if an error occurs 301 */ 302 protected void saxCoursePart(CoursePart coursePart) throws SAXException 303 { 304 AttributesImpl attrs = new AttributesImpl(); 305 attrs.addCDATAAttribute("id", coursePart.getId()); 306 attrs.addCDATAAttribute("title", coursePart.getTitle()); 307 _addAttrIfNotEmpty(attrs, "code", coursePart.getCode()); 308 309 XMLUtils.startElement(contentHandler, "coursepart", attrs); 310 311 XMLUtils.startElement(contentHandler, "attributes"); 312 _saxReferenceTableItem(coursePart.getNature(), CoursePart.NATURE, coursePart.getLanguage()); 313 314 _saxStructureViewIfExists(coursePart); 315 316 XMLUtils.endElement(contentHandler, "attributes"); 317 318 XMLUtils.endElement(contentHandler, "coursepart"); 319 } 320 321 /** 322 * SAX the 'structure' view if exists 323 * @param content the content 324 * @throws SAXException if an error occurs 325 */ 326 protected void _saxStructureViewIfExists(Content content) throws SAXException 327 { 328 View view = _cTypesHelper.getView("structure", content.getTypes(), content.getMixinTypes()); 329 if (view != null) 330 { 331 try 332 { 333 content.dataToSAX(contentHandler, view, DataContext.newInstance().withLocale(new Locale(content.getLanguage())).withEmptyValues(false)); 334 } 335 catch (BadItemTypeException | AmetysRepositoryException e) 336 { 337 throw new SAXException("Fail to sax the 'structure' view for content " + content.getId(), e); 338 } 339 } 340 } 341 342 /** 343 * SAX the common attributes for program item 344 * @param programItem the program item 345 * @param attrs the attributes 346 */ 347 protected void _saxCommonAttributes(ProgramItem programItem, AttributesImpl attrs) 348 { 349 attrs.addCDATAAttribute("title", ((Content) programItem).getTitle()); 350 attrs.addCDATAAttribute("id", programItem.getId()); 351 attrs.addCDATAAttribute("code", programItem.getCode()); 352 attrs.addCDATAAttribute("name", programItem.getName()); 353 } 354 355 /** 356 * SAX the item of a reference table 357 * @param itemId the item id 358 * @param tagName the tag name 359 * @param lang the language to use 360 * @throws SAXException if an error occurs while saxing 361 */ 362 protected void _saxReferenceTableItem(String itemId, String tagName, String lang) throws SAXException 363 { 364 OdfReferenceTableEntry item = Optional.ofNullable(itemId) 365 .filter(StringUtils::isNotEmpty) 366 .map(_odfReferenceTableHelper::getItem) 367 .orElse(null); 368 369 if (item != null) 370 { 371 AttributesImpl attrs = new AttributesImpl(); 372 attrs.addCDATAAttribute("id", item.getId()); 373 _addAttrIfNotEmpty(attrs, "code", item.getCode()); 374 375 XMLUtils.createElement(contentHandler, tagName, attrs, item.getLabel(lang)); 376 } 377 } 378 379 /** 380 * Add an attribute if its not null or empty. 381 * @param attrs The attributes 382 * @param attrName The attribute name 383 * @param attrValue The attribute value 384 */ 385 protected void _addAttrIfNotEmpty(AttributesImpl attrs, String attrName, String attrValue) 386 { 387 if (StringUtils.isNotEmpty(attrValue)) 388 { 389 attrs.addCDATAAttribute(attrName, attrValue); 390 } 391 } 392 393}