001/* 002 * Copyright 2017 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.cms.search.cocoon; 017 018import java.util.ArrayList; 019import java.util.Arrays; 020import java.util.Collection; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.avalon.framework.configuration.Configuration; 026import org.apache.avalon.framework.configuration.ConfigurationException; 027import org.apache.avalon.framework.configuration.DefaultConfiguration; 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.context.ContextException; 030import org.apache.avalon.framework.context.Contextualizable; 031import org.apache.avalon.framework.parameters.Parameters; 032import org.apache.avalon.framework.service.ServiceException; 033import org.apache.avalon.framework.service.ServiceManager; 034import org.apache.cocoon.ProcessingException; 035import org.apache.cocoon.acting.ServiceableAction; 036import org.apache.cocoon.components.LifecycleHelper; 037import org.apache.cocoon.environment.ObjectModelHelper; 038import org.apache.cocoon.environment.Redirector; 039import org.apache.cocoon.environment.Request; 040import org.apache.cocoon.environment.SourceResolver; 041import org.apache.cocoon.util.log.SLF4JLoggerAdapter; 042import org.slf4j.Logger; 043import org.slf4j.LoggerFactory; 044 045import org.ametys.cms.contenttype.ContentTypesHelper; 046import org.ametys.cms.data.type.ModelItemTypeConstants; 047import org.ametys.cms.repository.Content; 048import org.ametys.cms.search.ui.model.SearchUIModelHelper; 049import org.ametys.cms.search.ui.model.impl.MetadataSearchUIColumn; 050import org.ametys.core.cocoon.JSonReader; 051import org.ametys.core.util.ServerCommHelper; 052import org.ametys.plugins.repository.AmetysObjectResolver; 053import org.ametys.plugins.repository.model.CompositeDefinition; 054import org.ametys.plugins.repository.model.RepeaterDefinition; 055import org.ametys.runtime.model.ElementDefinition; 056import org.ametys.runtime.model.ModelItem; 057 058/** 059 * SAX the columns of a repeater for the search tool. 060 */ 061public class ModelRepeaterColumnsAction extends ServiceableAction implements Contextualizable 062{ 063 /** The servercomm helper */ 064 protected ServerCommHelper _serverCommHelper; 065 /** The Ametys object resolver */ 066 protected AmetysObjectResolver _resolver; 067 /** Content Types helper */ 068 protected ContentTypesHelper _contentTypesHelper; 069 /** Search UI Model helper */ 070 protected SearchUIModelHelper _searchUIModelHelper; 071 072 private ServiceManager _smanager; 073 private Context _context; 074 075 @Override 076 public void service(ServiceManager smanager) throws ServiceException 077 { 078 super.service(smanager); 079 _smanager = smanager; 080 _serverCommHelper = (ServerCommHelper) smanager.lookup(ServerCommHelper.ROLE); 081 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 082 _contentTypesHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE); 083 _searchUIModelHelper = (SearchUIModelHelper) smanager.lookup(SearchUIModelHelper.ROLE); 084 } 085 086 @Override 087 public void contextualize(Context context) throws ContextException 088 { 089 _context = context; 090 } 091 092 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 093 { 094 Request request = ObjectModelHelper.getRequest(objectModel); 095 Map<String, Object> results = new HashMap<>(); 096 097 Map<String, Object> jsParameters = _serverCommHelper.getJsParameters(); 098 099 String contentId = (String) jsParameters.get("contentId"); 100 String attributePath = (String) jsParameters.get("metadataPath"); 101 102 Content content = _resolver.resolveById(contentId); 103 if (content != null && attributePath != null && content.hasDefinition(attributePath)) 104 { 105 ModelItem modelItem = content.getDefinition(attributePath); 106 107 if (modelItem instanceof RepeaterDefinition) 108 { 109 List<Object> columns = new ArrayList<>(); 110 for (ModelItem subModelItem : ((RepeaterDefinition) modelItem).getModelItems()) 111 { 112 _addAttributeToResults(columns, subModelItem, subModelItem.getName(), Arrays.asList(content.getTypes())); 113 } 114 results.put("columns", columns); 115 } 116 } 117 118 results.put("success", true); 119 request.setAttribute(JSonReader.OBJECT_TO_READ, results); 120 return EMPTY_MAP; 121 } 122 123 private void _addAttributeToResults(List<Object> columns, ModelItem modelItem, String attributePath, Collection<String> contentTypes) throws ProcessingException, ConfigurationException 124 { 125 if (modelItem instanceof CompositeDefinition) 126 { 127 for (ModelItem subModelItem : ((CompositeDefinition) modelItem).getModelItems()) 128 { 129 _addAttributeToResults(columns, subModelItem, attributePath + "." + subModelItem.getName(), contentTypes); 130 } 131 } 132 else 133 { 134 MetadataSearchUIColumn metadataSearchUIColumn; 135 try 136 { 137 metadataSearchUIColumn = MetadataSearchUIColumn.class.newInstance(); 138 } 139 catch (InstantiationException | IllegalAccessException e) 140 { 141 throw new IllegalArgumentException("Cannot instanciate the class " + MetadataSearchUIColumn.class.getCanonicalName() + ". Check that there is a public constructor with no arguments."); 142 } 143 144 Logger logger = LoggerFactory.getLogger(MetadataSearchUIColumn.class); 145 try 146 { 147 Configuration columnConfig = _getColumnConfiguration(modelItem, contentTypes); 148 LifecycleHelper.setupComponent(metadataSearchUIColumn, new SLF4JLoggerAdapter(logger), _context, _smanager, columnConfig); 149 150 metadataSearchUIColumn.setId(attributePath); // Force field path to relative field path 151 metadataSearchUIColumn.setFieldPath(attributePath); // Force field path to relative field path 152 metadataSearchUIColumn.setEditable(_isEditionAllowed(modelItem)); // Edition only depends on final definition 153 metadataSearchUIColumn.setMultiple(modelItem instanceof ElementDefinition && ((ElementDefinition) modelItem).isMultiple()); // Multiple only depends on final definition 154 155 Map<String, Object> columnInfo = _searchUIModelHelper.getColumnInfo(metadataSearchUIColumn); 156 columns.add(columnInfo); 157 } 158 catch (Exception e) 159 { 160 throw new ConfigurationException("Unable to initialize search ui column for attribute '" + modelItem.getPath() + "'.", e); 161 } 162 finally 163 { 164 LifecycleHelper.dispose(metadataSearchUIColumn); 165 } 166 } 167 } 168 169 private boolean _isEditionAllowed(ModelItem modelItem) 170 { 171 return !ModelItemTypeConstants.RICH_TEXT_ELEMENT_TYPE_ID.equals(modelItem.getType().getId()); 172 } 173 174 private Configuration _getColumnConfiguration(ModelItem modelItem, Collection<String> contentTypes) 175 { 176 DefaultConfiguration columnConfig = new DefaultConfiguration("column"); 177 178 DefaultConfiguration metaConfig = new DefaultConfiguration("metadata"); 179 metaConfig.setAttribute("path", modelItem.getPath()); 180 columnConfig.addChild(metaConfig); 181 182 DefaultConfiguration cTypesConfig = new DefaultConfiguration("contentTypes"); 183 184 DefaultConfiguration baseCTypeConfig = new DefaultConfiguration("baseType"); 185 baseCTypeConfig.setAttribute("id", modelItem.getModel().getId()); 186 cTypesConfig.addChild(baseCTypeConfig); 187 188 for (String cTypeId : contentTypes) 189 { 190 DefaultConfiguration cTypeConfig = new DefaultConfiguration("type"); 191 cTypeConfig.setAttribute("id", cTypeId); 192 cTypesConfig.addChild(cTypeConfig); 193 } 194 195 columnConfig.addChild(cTypesConfig); 196 197 return columnConfig; 198 } 199}