001/* 002 * Copyright 2018 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.workflow; 017 018import java.util.List; 019import java.util.Map; 020import java.util.Optional; 021import java.util.function.Function; 022 023import org.apache.commons.lang3.StringUtils; 024 025import org.ametys.cms.repository.ContentQueryHelper; 026import org.ametys.cms.repository.ContentTypeExpression; 027import org.ametys.cms.repository.ModifiableContent; 028import org.ametys.cms.repository.ModifiableDefaultContent; 029import org.ametys.cms.workflow.CreateContentFunction; 030import org.ametys.odf.ProgramItem; 031import org.ametys.odf.course.CourseFactory; 032import org.ametys.odf.coursepart.CoursePart; 033import org.ametys.odf.coursepart.CoursePartFactory; 034import org.ametys.plugins.repository.AmetysObjectIterable; 035import org.ametys.plugins.repository.jcr.NameHelper.NameComputationMode; 036import org.ametys.plugins.repository.query.expression.AndExpression; 037import org.ametys.plugins.repository.query.expression.Expression; 038import org.ametys.plugins.repository.query.expression.Expression.Operator; 039import org.ametys.plugins.repository.query.expression.StringExpression; 040import org.ametys.runtime.i18n.I18nizableText; 041 042import com.opensymphony.workflow.WorkflowException; 043 044/** 045 * OSWorkflow function for creating a {@link CoursePart} content 046 */ 047public class CreateCoursePartFunction extends AbstractCreateODFContentFunction 048{ 049 /** Content name prefix for programs */ 050 public static final String CONTENT_NAME_PREFIX = "coursepart-"; 051 052 /** Constant for storing the catalog name to use into the transient variables map. */ 053 public static final String COURSE_HOLDER_KEY = AbstractCreateODFContentFunction.class.getName() + "$courseHolder"; 054 055 @Override 056 protected void _populateAdditionalData(Map transientVars, ModifiableContent content) throws WorkflowException 057 { 058 super._populateAdditionalData(transientVars, content); 059 060 if (content instanceof CoursePart coursePart) 061 { 062 /* Set the catalog */ 063 064 // Try to get the catalog from transient vars 065 _getValueFromTransientVars(transientVars, CONTENT_CATALOG_KEY) 066 // Used to get the catalog if the course part item is created by java code, like for the csv import. 067 .or(() -> _getValueFromInitialValueSupplier(transientVars, List.of(ProgramItem.CATALOG))) 068 // Set the catalog if value is present 069 .ifPresent(coursePart::setCatalog); 070 071 /* Set the course holder */ 072 073 // Try to get the course holder from transient vars 074 _getValueFromTransientVars(transientVars, COURSE_HOLDER_KEY) 075 // Used to get the courseHolder if the course part item is created by java code, like for the csv import. 076 .or(() -> _getCourseHolderFromInitialValueSupplier(transientVars)) 077 // Used to get the courseHolder if the course part item is created by java code from another content, like for the csv import. 078 .or(() -> _getValueFromTransientVars(transientVars, CreateContentFunction.PARENT_CONTEXT_VALUE)) 079 // Set the course holder if value is present 080 .ifPresent(ch -> coursePart.setValue(CoursePart.COURSE_HOLDER, ch)); 081 082 /* Set the code */ 083 084 // Used to get the code if the course part is created by java code, like for the csv import. 085 _getValueFromInitialValueSupplier(transientVars, List.of(ProgramItem.CODE)) 086 // Set the code if value is present 087 .ifPresent(coursePart::setCode); 088 089 // Generate a code if empty 090 String code = coursePart.getCode(); 091 if (StringUtils.isEmpty(code)) 092 { 093 coursePart.setCode(org.ametys.core.util.StringUtils.generateKey().toUpperCase()); 094 } 095 } 096 } 097 098 @Override 099 protected String _getContentNamePrefix() 100 { 101 return CONTENT_NAME_PREFIX; 102 } 103 104 @Override 105 protected String _getObjectType(Map transientVars, Map args) 106 { 107 return CoursePartFactory.COURSE_PART_NODETYPE; 108 } 109 110 @Override 111 protected NameComputationMode _getDefaultNameComputationMode() 112 { 113 return NameComputationMode.GENERATED_KEY; 114 } 115 116 @Override 117 public I18nizableText getLabel() 118 { 119 return new I18nizableText("plugin.odf", "PLUGINS_ODF_CREATE_COURSE_PART_CONTENT_FUNCTION_LABEL"); 120 } 121 122 private Optional<String> _getValueFromTransientVars(Map transientVars, String attributeName) 123 { 124 return Optional.ofNullable(transientVars.get(attributeName)) 125 .map(String.class::cast) 126 .filter(StringUtils::isNotEmpty); 127 } 128 129 @SuppressWarnings("unchecked") 130 private Optional<String> _getValueFromInitialValueSupplier(Map transientVars, List<String> attributePath) 131 { 132 return Optional.ofNullable(transientVars.get(CreateContentFunction.INITIAL_VALUE_SUPPLIER)) 133 .map(Function.class::cast) 134 .map(function -> function.apply(attributePath)) 135 .map(String.class::cast) 136 .filter(StringUtils::isNotEmpty); 137 } 138 139 private Optional<String> _getCourseHolderFromInitialValueSupplier(Map transientVars) 140 { 141 return _getValueFromInitialValueSupplier(transientVars, List.of(CoursePart.PARENT_COURSES, ProgramItem.CODE)) 142 .map(this::_searchCourseByCode); 143 } 144 145 private String _searchCourseByCode(String code) 146 { 147 Expression idExpression = new StringExpression(ProgramItem.CODE, Operator.EQ, code); 148 149 Expression expression = new AndExpression( 150 new ContentTypeExpression(Operator.EQ, CourseFactory.COURSE_CONTENT_TYPE), 151 idExpression); 152 153 String xPathQuery = ContentQueryHelper.getContentXPathQuery(expression); 154 AmetysObjectIterable<ModifiableDefaultContent> matchingContents = _resolver.query(xPathQuery); 155 if (matchingContents.getSize() == 1) 156 { 157 ModifiableDefaultContent course = matchingContents.iterator().next(); 158 return course.getId(); 159 } 160 return null; 161 } 162}