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;
040
041import com.opensymphony.workflow.WorkflowException;
042
043/**
044 * OSWorkflow function for creating a {@link CoursePart} content
045 */
046public class CreateCoursePartFunction extends AbstractCreateODFContentFunction
047{
048    /** Content name prefix for programs */
049    public static final String CONTENT_NAME_PREFIX = "coursepart-";
050    
051    /** Constant for storing the catalog name to use into the transient variables map. */
052    public static final String COURSE_HOLDER_KEY = AbstractCreateODFContentFunction.class.getName() + "$courseHolder";
053
054    @SuppressWarnings("unchecked")
055    @Override
056    protected void _populateAdditionalData(Map transientVars, ModifiableContent content) throws WorkflowException
057    {
058        super._populateAdditionalData(transientVars, content);
059        
060        Optional<String> catalogName = Optional.ofNullable(transientVars.get(CONTENT_CATALOG_KEY))
061                .map(String.class::cast)
062                .filter(StringUtils::isNotEmpty);
063
064        // Used to get the catalog if the course part item is created by java code, like for the csv import.
065        if (!catalogName.isPresent())
066        {
067            catalogName = Optional.ofNullable(transientVars.get(CreateContentFunction.INITIAL_VALUE_SUPPLIER))
068                    .map(Function.class::cast)
069                    .map(function -> function.apply(List.of(ProgramItem.CATALOG)))
070                    .map(String.class::cast)
071                    .filter(StringUtils::isNotEmpty);
072        }
073
074        if (catalogName.isPresent() && StringUtils.isNotEmpty(catalogName.get()) && content instanceof CoursePart)
075        {
076            ((CoursePart) content).setCatalog(catalogName.get());
077        }
078
079        Optional<String> courseHolder = Optional.ofNullable(transientVars.get(COURSE_HOLDER_KEY))
080                .map(String.class::cast)
081                .filter(StringUtils::isNotEmpty);
082
083        // Used to get the courseHolder if the course part item is created by java code, like for the csv import.
084        if (!courseHolder.isPresent())
085        {
086            courseHolder = Optional.ofNullable(transientVars.get(CreateContentFunction.INITIAL_VALUE_SUPPLIER))
087                    .map(Function.class::cast)
088                    .map(function -> function.apply(List.of(CoursePart.PARENT_COURSES, ProgramItem.CODE)))
089                    .map(String.class::cast)
090                    .map(code -> 
091                    {
092                        Expression idExpression = new StringExpression(ProgramItem.CODE, Operator.EQ, code);
093                        
094                        Expression expression = new AndExpression(
095                                new ContentTypeExpression(Operator.EQ, CourseFactory.COURSE_CONTENT_TYPE),
096                                idExpression);
097
098                        String xPathQuery = ContentQueryHelper.getContentXPathQuery(expression);
099                        AmetysObjectIterable<ModifiableDefaultContent> matchingContents = _resolver.query(xPathQuery);
100                        if (matchingContents.getSize() == 1)
101                        {
102                            ModifiableDefaultContent course = matchingContents.iterator().next();
103                            return course.getId();
104                        }
105                        return null;
106                    })
107                    .filter(StringUtils::isNotEmpty);
108        }
109
110        // Used to get the courseHolder if the course part item is created by java code, like for the csv import.
111        if (!courseHolder.isPresent())
112        {
113            courseHolder = Optional.ofNullable(transientVars.get(CreateContentFunction.PARENT_CONTEXT_VALUE))
114                    .map(String.class::cast)
115                    .filter(StringUtils::isNotEmpty);
116        }
117
118        if (courseHolder.isPresent() && StringUtils.isNotEmpty(courseHolder.get()) && content instanceof CoursePart)
119        {
120            content.setValue(CoursePart.COURSE_HOLDER, courseHolder.get());
121        }
122        
123        String code = content.getValue(CoursePart.CODE);
124        if (StringUtils.isEmpty(code))
125        {
126            content.setValue(CoursePart.CODE, org.ametys.core.util.StringUtils.generateKey().toUpperCase());
127        }
128    }
129    
130    @Override
131    protected String _getContentNamePrefix()
132    {
133        return CONTENT_NAME_PREFIX;
134    }
135    
136    @Override
137    protected String _getObjectType(Map transientVars, Map args)
138    {
139        return CoursePartFactory.COURSE_PART_NODETYPE;
140    }
141    
142    @Override
143    protected NameComputationMode _getDefaultNameComputationMode()
144    {
145        return NameComputationMode.GENERATED_KEY;
146    }
147}