001/* 002 * Copyright 2015 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; 017 018import java.time.ZonedDateTime; 019import java.util.ArrayList; 020import java.util.List; 021import java.util.Optional; 022import java.util.Set; 023 024import javax.xml.transform.TransformerFactory; 025import javax.xml.transform.dom.DOMResult; 026import javax.xml.transform.sax.SAXTransformerFactory; 027import javax.xml.transform.sax.TransformerHandler; 028 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.avalon.framework.service.Serviceable; 032import org.apache.cocoon.xml.XMLUtils; 033import org.apache.commons.lang3.StringUtils; 034import org.w3c.dom.Node; 035import org.w3c.dom.NodeList; 036 037import org.ametys.cms.repository.Content; 038import org.ametys.core.util.dom.AmetysNodeList; 039import org.ametys.odf.enumeration.OdfReferenceTableEntry; 040import org.ametys.odf.enumeration.OdfReferenceTableHelper; 041import org.ametys.odf.orgunit.OrgUnit; 042import org.ametys.odf.orgunit.RootOrgUnitProvider; 043import org.ametys.odf.program.AbstractProgram; 044import org.ametys.odf.program.Program; 045import org.ametys.odf.program.SubProgram; 046import org.ametys.odf.xslt.OdfReferenceTableElement; 047import org.ametys.odf.xslt.ProgramElement; 048import org.ametys.odf.xslt.SubProgramElement; 049import org.ametys.plugins.repository.AmetysObjectResolver; 050import org.ametys.plugins.repository.AmetysRepositoryException; 051import org.ametys.plugins.repository.data.holder.group.impl.ModifiableModelAwareRepeater; 052import org.ametys.plugins.repository.data.holder.group.impl.ModifiableModelAwareRepeaterEntry; 053import org.ametys.runtime.config.Config; 054 055/** 056 * Helper component to be used from XSL stylesheets. 057 */ 058public class OdfXSLTHelper implements Serviceable 059{ 060 /** The ODF helper */ 061 protected static ODFHelper _odfHelper; 062 /** The ODF reference helper */ 063 protected static OdfReferenceTableHelper _odfRefTableHelper; 064 /** The Ametys resolver */ 065 protected static AmetysObjectResolver _ametysObjectResolver; 066 /** The orgunit root provider */ 067 protected static RootOrgUnitProvider _rootOrgUnitProvider; 068 069 @Override 070 public void service(ServiceManager smanager) throws ServiceException 071 { 072 _odfHelper = (ODFHelper) smanager.lookup(ODFHelper.ROLE); 073 _odfRefTableHelper = (OdfReferenceTableHelper) smanager.lookup(OdfReferenceTableHelper.ROLE); 074 _ametysObjectResolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 075 _rootOrgUnitProvider = (RootOrgUnitProvider) smanager.lookup(RootOrgUnitProvider.ROLE); 076 } 077 078 /** 079 * Get the label associated with the degree key 080 * @param cdmValue The code of degree 081 * @return The label of degree or code if not found 082 */ 083 public static String degreeLabel (String cdmValue) 084 { 085 return degreeLabel(cdmValue, Config.getInstance().getValue("odf.programs.lang")); 086 } 087 088 /** 089 * Get the code associated with the given reference table's entry 090 * @param tableRefEntryId The id of entry 091 * @return the code or <code>null</code> if not found 092 */ 093 public static String getCode (String tableRefEntryId) 094 { 095 try 096 { 097 Content content = _ametysObjectResolver.resolveById(tableRefEntryId); 098 return content.getValue(OdfReferenceTableEntry.CODE); 099 } 100 catch (AmetysRepositoryException e) 101 { 102 return null; 103 } 104 } 105 106 /** 107 * Get the id of reference table's entry 108 * @param tableRefId The id of content type 109 * @param code The code 110 * @return the id or <code>null</code> if not found 111 */ 112 public static String getEntryId (String tableRefId, String code) 113 { 114 OdfReferenceTableEntry entry = _odfRefTableHelper.getItemFromCode(tableRefId, code); 115 if (entry != null) 116 { 117 return entry.getId(); 118 } 119 return null; 120 } 121 122 /** 123 * Get the label associated with the degree key 124 * @param cdmValue The cdm value of degree 125 * @param lang The language 126 * @return The label of degree or empty string if not found 127 */ 128 public static String degreeLabel (String cdmValue, String lang) 129 { 130 return Optional 131 .ofNullable(_odfRefTableHelper.getItemFromCDM(OdfReferenceTableHelper.DEGREE, cdmValue)) 132 .map(degree -> degree.getLabel(lang)) 133 .orElse(StringUtils.EMPTY); 134 } 135 136 /** 137 * Get the whole structure of a subprogram, including the structure of child subprograms 138 * @param subprogramId The id of subprogram 139 * @return Node with the subprogram structure 140 */ 141 public static Node getSubProgramStructure (String subprogramId) 142 { 143 SubProgram subProgram = _ametysObjectResolver.resolveById(subprogramId); 144 return new SubProgramElement(subProgram, _ametysObjectResolver); 145 } 146 147 /** 148 * Get the structure of a subprogram, including the structure of child subprograms until the given depth 149 * @param subprogramId The id of subprogram 150 * @param depth Set a positive number to get structure of child subprograms until given depth. Set a negative number to get the whole structure recursively, including the structure of child subprograms. This parameter concerns only subprograms. 151 * @return Node with the subprogram structure 152 */ 153 public static Node getSubProgramStructure (String subprogramId, int depth) 154 { 155 SubProgram subProgram = _ametysObjectResolver.resolveById(subprogramId); 156 return new SubProgramElement(subProgram, depth, null, _ametysObjectResolver); 157 } 158 159 /** 160 * Get the parent program information 161 * @param subprogramId The id of subprogram 162 * @return a node for each program's information 163 */ 164 public static NodeList getParentProgram (String subprogramId) 165 { 166 return getParentProgramStructure(subprogramId, 0); 167 } 168 169 /** 170 * Get the certification label of a {@link AbstractProgram}. 171 * Returns null if the program is not certified. 172 * @param abstractProgramId the id of program or subprogram 173 * @return the certification label 174 */ 175 public static String getCertificationLabel(String abstractProgramId) 176 { 177 AbstractProgram abstractProgram = _ametysObjectResolver.resolveById(abstractProgramId); 178 if (abstractProgram.isCertified()) 179 { 180 String degreeId = null; 181 if (abstractProgram instanceof Program) 182 { 183 degreeId = abstractProgram.getDegree(); 184 } 185 else if (abstractProgram instanceof SubProgram) 186 { 187 // Get degree from parent 188 Set<Program> rootPrograms = abstractProgram.getRootPrograms(); 189 if (rootPrograms.size() > 0) 190 { 191 degreeId = rootPrograms.iterator().next().getDegree(); 192 } 193 } 194 195 if (StringUtils.isNotEmpty(degreeId)) 196 { 197 Content degree = _ametysObjectResolver.resolveById(degreeId); 198 return degree.getValue("certificationLabel"); 199 } 200 } 201 202 return null; 203 } 204 205 /** 206 * Get the program information 207 * @param programId The id of program 208 * @return Node with the program's information 209 */ 210 public static Node getProgram (String programId) 211 { 212 return getProgramStructure(programId, 0); 213 } 214 215 /** 216 * Get the structure of a parent programs, including the structure of child subprograms until the given depth. 217 * @param subprogramId The id of subprogram 218 * @param depth Set a positive number to get structure of child subprograms until given depth. Set a negative number to get the whole structure recursively, including the structure of child subprograms. This parameter concerns only subprograms. 219 * @return a node for each program's structure 220 */ 221 public static NodeList getParentProgramStructure (String subprogramId, int depth) 222 { 223 List<ProgramElement> programs = new ArrayList<>(); 224 225 SubProgram subProgram = _ametysObjectResolver.resolveById(subprogramId); 226 227 Set<Program> rootPrograms = subProgram.getRootPrograms(); 228 if (rootPrograms.size() > 0) 229 { 230 programs.add(new ProgramElement(rootPrograms.iterator().next(), depth, null, _ametysObjectResolver)); 231 } 232 233 return new AmetysNodeList(programs); 234 } 235 236 /** 237 * Get the structure of a program until the given depth. 238 * @param programId The id of program 239 * @param depth Set a positive number to get structure of child subprograms until given depth. Set a negative number to get the whole structure recursively, including the structure of child subprograms. This parameter concerns only subprograms. 240 * @return Node with the program structure 241 */ 242 public static Node getProgramStructure (String programId, int depth) 243 { 244 Program program = _ametysObjectResolver.resolveById(programId); 245 return new ProgramElement(program, depth, null, _ametysObjectResolver); 246 } 247 248 /** 249 * Get the items of a reference table 250 * @param tableRefId the id of reference table 251 * @param lang the language to use for labels 252 * @return the items 253 */ 254 public static Node getTableRefItems(String tableRefId, String lang) 255 { 256 return getTableRefItems(tableRefId, lang, false); 257 } 258 259 /** 260 * Get the items of a reference table 261 * @param tableRefId the id of reference table 262 * @param lang the language to use for labels 263 * @param ordered true to sort items by 'order' attribute 264 * @return the items 265 */ 266 public static Node getTableRefItems(String tableRefId, String lang, boolean ordered) 267 { 268 return new OdfReferenceTableElement(tableRefId, _odfRefTableHelper, lang, ordered); 269 } 270 271 /** 272 * Get the id of root orgunit 273 * @return The id of root 274 */ 275 public static String getRootOrgUnitId() 276 { 277 return _rootOrgUnitProvider.getRootId(); 278 } 279 280 /** 281 * Get the id of the first orgunit matching the given UAI code 282 * @param uaiCode the UAI code 283 * @return the id of orgunit or null if not found 284 */ 285 public static String getOrgUnitIdByUAICode(String uaiCode) 286 { 287 return Optional.ofNullable(uaiCode) 288 .map(_odfHelper::getOrgUnitByUAICode) 289 .map(OrgUnit::getId) 290 .orElse(null); 291 } 292 293 /** 294 * Get the more recent educational booklet for one subprogram 295 * @param subProgramId the subprogram id 296 * @return the pdf as an ametys node list 297 */ 298 public static AmetysNodeList getEducationalBooklet(String subProgramId) 299 { 300 try 301 { 302 SubProgram subProgram = _ametysObjectResolver.resolveById(subProgramId); 303 if (subProgram.hasValue("educational-booklets")) 304 { 305 ModifiableModelAwareRepeater repeater = subProgram.getRepeater("educational-booklets"); 306 ZonedDateTime dateToCompare = null; 307 ModifiableModelAwareRepeaterEntry entry = null; 308 for (ModifiableModelAwareRepeaterEntry repeaterEntry : repeater.getEntries()) 309 { 310 ZonedDateTime date = repeaterEntry.getValue("date"); 311 if (dateToCompare == null || date.isAfter(dateToCompare)) 312 { 313 dateToCompare = date; 314 entry = repeaterEntry; 315 } 316 } 317 318 if (entry != null) 319 { 320 SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); 321 TransformerHandler th = saxTransformerFactory.newTransformerHandler(); 322 323 DOMResult result = new DOMResult(); 324 th.setResult(result); 325 326 th.startDocument(); 327 XMLUtils.startElement(th, "value"); 328 if (entry.hasValue("pdf")) 329 { 330 subProgram.dataToSAX(th, "educational-booklets[" + entry.getPosition() + "]/pdf"); 331 } 332 XMLUtils.endElement(th, "value"); 333 th.endDocument(); 334 335 List<Node> values = new ArrayList<>(); 336 337 // #getChildNodes() returns a NodeList that contains the value(s) saxed 338 // we cannot returns directly this NodeList because saxed values should be wrapped into a <value> tag. 339 NodeList childNodes = result.getNode().getFirstChild().getChildNodes(); 340 for (int i = 0; i < childNodes.getLength(); i++) 341 { 342 Node n = childNodes.item(i); 343 values.add(n); 344 } 345 346 return new AmetysNodeList(values); 347 } 348 } 349 } 350 catch (Exception e) 351 { 352 return null; 353 } 354 355 return null; 356 } 357 358 /** 359 * Count the hours accumulation in the {@link ProgramItem} 360 * @param contentId The id of the {@link ProgramItem} 361 * @return The hours accumulation 362 */ 363 public static Double getCumulatedHours(String contentId) 364 { 365 return _odfHelper.getCumulatedHours(_ametysObjectResolver.<ProgramItem>resolveById(contentId)); 366 } 367}