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.plugins.odfsync.cdmfr.components.impl;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.List;
022import java.util.Map;
023import java.util.Set;
024
025import javax.jcr.RepositoryException;
026
027import org.apache.avalon.framework.configuration.Configuration;
028import org.apache.avalon.framework.configuration.ConfigurationException;
029import org.apache.commons.lang3.StringUtils;
030import org.slf4j.Logger;
031import org.w3c.dom.Document;
032import org.w3c.dom.Node;
033
034import org.ametys.cms.content.external.ExternalizableMetadataHelper;
035import org.ametys.cms.contenttype.ContentType;
036import org.ametys.cms.repository.ContentQueryHelper;
037import org.ametys.cms.repository.ContentTypeExpression;
038import org.ametys.cms.repository.LanguageExpression;
039import org.ametys.cms.repository.ModifiableDefaultContent;
040import org.ametys.odf.ProgramItem;
041import org.ametys.odf.enumeration.OdfReferenceTableHelper;
042import org.ametys.odf.program.AbstractProgram;
043import org.ametys.odf.program.ProgramFactory;
044import org.ametys.odf.program.ProgramPart;
045import org.ametys.odf.program.SubProgram;
046import org.ametys.odf.program.TraversableProgramPart;
047import org.ametys.plugins.repository.AmetysObjectIterable;
048import org.ametys.plugins.repository.query.expression.AndExpression;
049import org.ametys.plugins.repository.query.expression.Expression;
050import org.ametys.plugins.repository.query.expression.Expression.Operator;
051import org.ametys.plugins.repository.query.expression.StringExpression;
052import org.ametys.runtime.config.Config;
053
054/**
055 * Component to import a CDM-fr input stream from a remote server with co-accredited mode.
056 */
057public class CoAccreditedRemoteImportCDMFrComponent extends RemoteImportCDMFrComponent
058{
059    /** The list of metadata to copy for mention program */
060    protected Set<String> _mentionMetadataPaths;
061    
062    private Map<String, String> _degree2MentionMap;
063    private ContentType _mentionContentType;
064    private String _mention;
065    private String _programToLinkCode;
066
067    @Override
068    public void initialize() throws Exception
069    {
070        super.initialize();
071
072        _degree2MentionMap = new HashMap<>();
073        _degree2MentionMap.put(Config.getInstance().getValueAsString("odf.programs.degree.license"), OdfReferenceTableHelper.MENTION_LICENCE);
074        _degree2MentionMap.put(Config.getInstance().getValueAsString("odf.programs.degree.licensepro"), OdfReferenceTableHelper.MENTION_LICENCEPRO);
075        _degree2MentionMap.put(Config.getInstance().getValueAsString("odf.programs.degree.master"), OdfReferenceTableHelper.MENTION_MASTER);
076
077        _mentionContentType = _contentTypeEP.getExtension(getProgramWfDescription().getContentType());
078    }
079    
080    @Override
081    public void configure(Configuration configuration) throws ConfigurationException
082    {
083        super.configure(configuration);
084
085        _mentionMetadataPaths = new HashSet<>();
086        Configuration mentionConf = configuration.getChild("mention");
087        if (mentionConf != null)
088        {
089            Configuration metadatas = mentionConf.getChild("metadata-to-copy");
090            if (metadatas != null)
091            {
092                for (Configuration metadataConf : metadatas.getChildren())
093                {
094                    String metadataPath = metadataConf.getAttribute("path");
095                    _mentionMetadataPaths.add(metadataPath);
096                }
097            }
098        }
099    }
100
101    @Override
102    protected void additionalParameters(Map<String, Object> parameters)
103    {
104        _mention = null;
105        super.additionalParameters(parameters);
106    }
107
108    @Override
109    protected ModifiableDefaultContent _importOrSynchronizeContent(Document doc, Node contentNode, ContentWorkflowDescription wfDescription, String title, String lang, String catalog, String syncCode, Logger logger)
110    {
111        ContentWorkflowDescription contentWfDescription = wfDescription;
112        
113        if (contentNode.getLocalName().equals(_TAG_PROGRAM))
114        {
115            String educationKind = _xPathProcessor.evaluateAsString(contentNode, AbstractProgram.EDUCATION_KIND);
116            String mention = _xPathProcessor.evaluateAsString(contentNode, AbstractProgram.MENTION);
117
118            // This is a co-accredited program
119            if (StringUtils.isNotBlank(mention) && "parcours".equals(educationKind))
120            {
121                // Change the workflow description of the program to subprogram
122                contentWfDescription = getSubProgramWfDescription();
123
124                // Get or create the mention from the program
125                _mention = _getOrCreateMention(doc, contentNode, mention, lang, catalog, logger);
126                
127                // Store the content to link to the mention
128                _programToLinkCode = syncCode;
129            }
130        }
131        
132        return super._importOrSynchronizeContent(doc, contentNode, contentWfDescription, title, lang, catalog, syncCode, logger);
133    }
134    
135    private String _getOrCreateMention(Document doc, Node contentNode, String mentionCode, String lang, String catalog, Logger logger)
136    {
137        String degreeCode = _xPathProcessor.evaluateAsString(contentNode, AbstractProgram.DEGREE);
138        
139        String mentionType = _degree2MentionMap.get(degreeCode);
140        if (mentionType == null)
141        {
142            String mentionId = _getIdFromCDMThenCode(mentionType, mentionCode);
143            if (mentionId == null)
144            {
145                ModifiableDefaultContent mention = _getMention(mentionCode, degreeCode, lang, catalog);
146                if (mention == null)
147                {
148                    mention = _createMention(doc, contentNode, mentionId, catalog, lang, logger);
149                }
150                return mention != null ? mention.getId() : null;
151            }
152            else
153            {
154                logger.error("Il n'y a pas de code associé à la mention {}. La formation n'a pas été importée.", mentionCode);
155                _nbError++;
156            }
157        }
158        else
159        {
160            logger.error("Il n'y a pas de type de mention (licence, licence pro, master) associée au diplôme {}. La formation n'a pas été importée.", degreeCode);
161            _nbError++;
162        }
163        
164        return null;
165    }
166    
167    private ModifiableDefaultContent _createMention(Document doc, Node contentNode, String mentionId, String catalog, String lang, Logger logger)
168    {
169        String contentTitle = _odfRefTableHelper.getItemLabel(null, mentionId, lang);
170        ContentWorkflowDescription wfDescription = getProgramWfDescription();
171        Map<String, Object> resultMap = _synchroComponent.createContentAction(wfDescription.getContentType(), wfDescription.getWorkflowName(), wfDescription.getInitialActionId(), lang, contentTitle, _contentPrefix, logger);
172        if ((boolean) resultMap.getOrDefault("error", false))
173        {
174            _nbError++;
175        }
176        
177        ModifiableDefaultContent mention = (ModifiableDefaultContent) resultMap.get("content");
178
179        if (mention != null)
180        {
181            boolean hasChanges = false;
182            if (catalog != null)
183            {
184                hasChanges = ExternalizableMetadataHelper.setMetadata(mention.getMetadataHolder(), ProgramItem.METADATA_CATALOG, catalog);
185            }
186
187            hasChanges = _synchronizeMentionMetadata(doc, contentNode, mention, AbstractProgram.DEGREE, lang, logger) || hasChanges;
188            hasChanges = _synchronizeMentionMetadata(doc, contentNode, mention, AbstractProgram.MENTION, lang, logger) || hasChanges;
189            hasChanges = _synchronizeMentionMetadata(doc, contentNode, mention, AbstractProgram.DOMAIN, lang, logger) || hasChanges;
190            
191            for (String metadataPath : _mentionMetadataPaths)
192            {
193                hasChanges = _synchronizeMentionMetadata(doc, contentNode, mention, metadataPath, lang, logger) || hasChanges;
194            }
195            
196            _saveContentChanges(mention, wfDescription.getContentType(), hasChanges, logger);
197        }
198        
199        return mention;
200    }
201    
202    private ModifiableDefaultContent _getMention(String mentionCode, String degreeCode, String lang, String catalog)
203    {
204        List<Expression> expList = new ArrayList<>();
205        expList.add(new ContentTypeExpression(Operator.EQ, ProgramFactory.PROGRAM_CONTENT_TYPE));
206        expList.add(new LanguageExpression(Operator.EQ, lang));
207        expList.add(new StringExpression(ProgramItem.METADATA_CATALOG, Operator.EQ, catalog));
208        expList.add(new StringExpression(AbstractProgram.DEGREE, Operator.EQ, degreeCode));
209        expList.add(new StringExpression(AbstractProgram.MENTION, Operator.EQ, mentionCode));
210
211        AndExpression andExp = new AndExpression(expList.toArray(new Expression[expList.size()]));
212        String xPathQuery = ContentQueryHelper.getContentXPathQuery(andExp);
213
214        AmetysObjectIterable<ModifiableDefaultContent> contents = _resolver.query(xPathQuery);
215        
216        if (contents.getSize() > 0)
217        {
218            return contents.iterator().next();
219        }
220        
221        return null;
222    }
223
224    @Override
225    protected void additionalOperationsBeforeSave(ModifiableDefaultContent content, Logger logger) throws RepositoryException
226    {
227        if (_mention != null && content instanceof SubProgram && content.getMetadataHolder().getString(getIdField()).equals(_programToLinkCode))
228        {
229            boolean hasChanges = false;
230            
231            ModifiableDefaultContent mentionContent = _resolver.resolveById(_mention);
232            
233            // Relier le programme à la mention
234            hasChanges = _synchroComponent.updateRelation(mentionContent.getMetadataHolder(), TraversableProgramPart.METADATA_CHILD_PROGRAM_PARTS, content, false) || hasChanges;
235            if (_synchroComponent.updateRelation(content.getMetadataHolder(), ProgramPart.METADATA_PARENT_PROGRAM_PARTS, _mention, false))
236            {
237                _saveContentChanges(content, getSubProgramWfDescription().getContentType(), true, logger);
238            }
239
240            // Mettre les composantes de la mention à jour
241            List<String> orgunits = ((SubProgram) content).getOrgUnits();
242            for (String orgunit : orgunits)
243            {
244                hasChanges = _synchroComponent.updateRelation(mentionContent.getMetadataHolder(), AbstractProgram.ORG_UNITS_REFERENCES, orgunit, false) || hasChanges;
245            }
246            
247            if (hasChanges)
248            {
249                _saveContentChanges(mentionContent, _mentionContentType.getId(), hasChanges, logger);
250            }
251        }
252    }
253    
254    private boolean _synchronizeMentionMetadata(Document doc, Node contentNode, ModifiableDefaultContent mention, String metadataPath, String lang, Logger logger)
255    {
256        Node metadataNode = _xPathProcessor.selectSingleNode(contentNode, metadataPath);
257        if (metadataNode != null)
258        {
259            return _synchronizeMetadata(doc, metadataNode, mention, metadataPath, metadataPath, _mentionContentType, lang, logger);
260        }
261        return false;
262    }
263}