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}