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