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.io.File;
019import java.io.FileFilter;
020import java.util.HashMap;
021import java.util.Map;
022
023import org.apache.avalon.framework.configuration.Configuration;
024import org.apache.avalon.framework.configuration.ConfigurationException;
025import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
026import org.apache.avalon.framework.context.Context;
027import org.apache.avalon.framework.context.ContextException;
028import org.apache.avalon.framework.context.Contextualizable;
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.Constants;
033import org.apache.cocoon.util.log.SLF4JLoggerAdapter;
034import org.apache.commons.lang3.StringUtils;
035
036import org.ametys.cms.content.indexing.solr.observation.ObserverHelper;
037import org.ametys.cms.contenttype.ContentType;
038import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
039import org.ametys.cms.repository.ContentQueryHelper;
040import org.ametys.cms.repository.ContentTypeExpression;
041import org.ametys.cms.repository.ModifiableContent;
042import org.ametys.cms.repository.WorkflowAwareContent;
043import org.ametys.cms.workflow.AbstractContentWorkflowComponent;
044import org.ametys.cms.workflow.ContentWorkflowHelper;
045import org.ametys.cms.workflow.CreateContentFunction;
046import org.ametys.core.engine.BackgroundEngineHelper;
047import org.ametys.core.util.I18nUtils;
048import org.ametys.plugins.repository.AmetysObject;
049import org.ametys.plugins.repository.AmetysObjectIterable;
050import org.ametys.plugins.repository.AmetysObjectIterator;
051import org.ametys.plugins.repository.AmetysObjectResolver;
052import org.ametys.plugins.repository.query.expression.AndExpression;
053import org.ametys.plugins.repository.query.expression.Expression.Operator;
054import org.ametys.plugins.repository.query.expression.StringExpression;
055import org.ametys.runtime.config.Config;
056import org.ametys.runtime.i18n.I18nizableText;
057import org.ametys.runtime.plugin.Init;
058import org.ametys.runtime.plugin.component.AbstractLogEnabled;
059
060import com.opensymphony.workflow.WorkflowException;
061
062/**
063 * This init class populates reference tables contents base on WEB-INF/params/odf files
064 *
065 */
066public class PopulateOdfTableRef extends AbstractLogEnabled implements Init, Serviceable, Contextualizable
067{
068    private AmetysObjectResolver _resolver;
069    private org.apache.cocoon.environment.Context _cocoonContext;
070    private I18nUtils _i18nUtils;
071    private ContentTypeExtensionPoint _cTypeExtensionPoint;
072    private ServiceManager _manager;
073    private ContentWorkflowHelper _workflowHelper;
074
075    @Override
076    public void service(ServiceManager manager) throws ServiceException
077    {
078        _manager = manager;
079        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
080        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
081        _cTypeExtensionPoint = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
082        _workflowHelper = (ContentWorkflowHelper) manager.lookup(ContentWorkflowHelper.ROLE);
083    }
084    
085    public void contextualize(Context context) throws ContextException
086    {
087        _cocoonContext = (org.apache.cocoon.environment.Context) context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT);
088    }
089    
090    public void init() throws Exception
091    {
092        File file = new File(_cocoonContext.getRealPath("/WEB-INF/param/odf"));
093        
094        if (file.exists() && file.isDirectory())
095        {
096            Map<String, Object> environmentInformation = null;
097            try
098            {
099                // Enter in a background environment as i18nUtils can not be used outside a request
100                // because of XMLBundleResource uses SourceUtil.toSAX(..) to parse its values.
101                environmentInformation = BackgroundEngineHelper.createAndEnterEngineEnvironment(_manager, _cocoonContext, new SLF4JLoggerAdapter(getLogger()));
102                
103                ObserverHelper.suspendObservationForIndexation();
104                
105                File[] xmlFiles = file.listFiles(new FileFilter()
106                {
107                    public boolean accept(File child)
108                    {
109                        return child.isFile() && child.getName().endsWith(".xml");
110                    }
111                });
112                
113                for (File xmlFile : xmlFiles)
114                {
115                    int count = 0;
116                    String fileName = xmlFile.getName();
117                    
118                    String cTypeId = _transform2ContentTypeId (fileName);
119                    
120                    // Check that the simple content type exists
121                    if (_cTypeExtensionPoint.hasExtension(cTypeId))
122                    {
123                        Configuration configuration = new DefaultConfigurationBuilder().buildFromFile(xmlFile);
124                        for (Configuration itemConfig : configuration.getChildren("item"))
125                        {
126                            boolean hasSubEntries = itemConfig.getChild("subitem", false) != null;
127                            count += _createEntryIfNotExists(cTypeId + (hasSubEntries ? "Category" : ""), itemConfig, null);
128                        }
129                        
130                        getLogger().info("Create {} entries for content type '{}'", count, cTypeId);
131                    }
132                    else
133                    {
134                        getLogger().info("Content type '{}' is not a valid content type.", cTypeId);
135                    }
136                }
137            }
138            finally
139            {
140                // Leave the environment.
141                if (environmentInformation != null)
142                {
143                    BackgroundEngineHelper.leaveEngineEnvironment(environmentInformation);
144                }
145                
146                ObserverHelper.restartObservationForIndexation();
147            }
148        }
149    }
150    
151    private String _transform2ContentTypeId (String filename)
152    {
153        int index = filename.lastIndexOf(".");
154        String id = filename.substring(0, index);
155        
156        StringBuilder sb = new StringBuilder();
157        sb.append("odf-enumeration.");
158        
159        for (String segment : id.split("_"))
160        {
161            sb.append(StringUtils.capitalize(segment));
162        }
163        
164        return sb.toString();
165    }
166    
167    private int _createEntryIfNotExists (String cTypeId, Configuration itemConfig, String parentCategoryId) throws WorkflowException, ConfigurationException
168    {
169        int count = 0;
170        
171        String code = itemConfig.getAttribute("code");
172        String i18nKey = itemConfig.getAttribute("i18n-key");
173        String cdmValue = itemConfig.getAttribute("cdmValue", null);
174        
175        ContentTypeExpression cTypeExpr = new ContentTypeExpression(Operator.EQ, cTypeId);
176        StringExpression codeExpr = new StringExpression("code", Operator.EQ, code);
177        
178        String xpathQuery = ContentQueryHelper.getContentXPathQuery(new AndExpression(cTypeExpr, codeExpr));
179        AmetysObjectIterable<AmetysObject> contents = _resolver.query(xpathQuery);
180        AmetysObjectIterator<AmetysObject> it = contents.iterator();
181
182        ContentType contentType = _cTypeExtensionPoint.getExtension(cTypeId);
183        
184        ModifiableContent entry = null;
185        if (!it.hasNext())
186        {
187            String titleFR = _i18nUtils.translate(new I18nizableText("application", i18nKey), "fr");
188            
189            if (StringUtils.isEmpty(titleFR))
190            {
191                getLogger().error("The reference table item with the code '{}' of type '{}' can't be migrated because it's I18N key '{}' is not defined for french.", code, cTypeId, i18nKey);
192            }
193            else
194            {
195                String titleEN = _i18nUtils.translate(new I18nizableText("application", i18nKey), "en");
196                
197                Map<String, String> titleVariants = new HashMap<>();
198                titleVariants.put("fr", titleFR);
199                if (titleEN != null)
200                {
201                    titleVariants.put("en", titleEN);
202                }
203                
204                Map<String, Object> inputs = new HashMap<>();
205                if (!contentType.isMultilingual())
206                {
207                    inputs.put(CreateContentFunction.CONTENT_LANGUAGE_KEY, Config.getInstance().getValue("odf.programs.lang")); 
208                }
209                Map<String, Object> result = _workflowHelper.createContent("reference-table", 1, titleFR, titleVariants, new String[] {cTypeId}, new String[0], null, null, inputs);
210                
211                entry = (ModifiableContent) result.get(AbstractContentWorkflowComponent.CONTENT_KEY);
212                entry.getMetadataHolder().setMetadata("code", code);
213                
214                if (StringUtils.isNotEmpty(cdmValue))
215                {
216                    entry.getMetadataHolder().setMetadata("cdmValue", cdmValue);
217                }
218                
219                if (parentCategoryId != null)
220                {
221                    entry.getMetadataHolder().setMetadata("category", parentCategoryId);
222                }
223                entry.saveChanges();
224                _workflowHelper.doAction((WorkflowAwareContent) entry, 22);
225                
226                count++;
227            }
228        }
229        else
230        {
231            entry = (ModifiableContent) it.next();
232        }
233        
234        for (Configuration childConfig : itemConfig.getChildren("subitem"))
235        {
236            String subCType = cTypeId.endsWith("Category") ? StringUtils.substringBefore(cTypeId, "Category") : cTypeId;
237            if (_cTypeExtensionPoint.hasExtension(subCType))
238            {
239                count += _createEntryIfNotExists(subCType, childConfig, entry.getId());
240            }
241            else
242            {
243                getLogger().error("Unknow content type of id {}", subCType);
244            }
245        }
246        
247        return count;
248        
249    }
250
251}