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