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.cms.search.systemprop; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027import org.apache.avalon.framework.configuration.Configuration; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.cocoon.xml.XMLUtils; 031import org.apache.commons.lang.BooleanUtils; 032import org.apache.solr.common.SolrInputDocument; 033import org.xml.sax.ContentHandler; 034import org.xml.sax.SAXException; 035 036import org.ametys.cms.content.indexing.solr.SolrContentIndexer; 037import org.ametys.cms.content.indexing.solr.SolrFieldNames; 038import org.ametys.cms.contenttype.MetadataType; 039import org.ametys.cms.repository.Content; 040import org.ametys.cms.search.SearchField; 041import org.ametys.cms.search.model.SystemProperty; 042import org.ametys.cms.search.query.Query; 043import org.ametys.cms.search.query.Query.Operator; 044import org.ametys.cms.search.query.TagQuery; 045import org.ametys.cms.search.solr.field.TagSearchField; 046import org.ametys.cms.search.solr.schema.FieldDefinition; 047import org.ametys.cms.search.solr.schema.SchemaDefinition; 048import org.ametys.cms.search.solr.schema.SchemaHelper; 049import org.ametys.cms.tag.Tag; 050import org.ametys.cms.tag.TagHelper; 051import org.ametys.cms.tag.TagProviderExtensionPoint; 052import org.ametys.core.util.JSONUtils; 053import org.ametys.runtime.i18n.I18nizableText; 054 055/** 056 * {@link SystemProperty} which represents the tags of a Content. 057 */ 058public class TagsSystemProperty extends AbstractSystemProperty 059{ 060 /** The tag provider extension point. */ 061 protected TagProviderExtensionPoint _tagProviderEP; 062 /** The JSON utils */ 063 protected JSONUtils _jsonUtils; 064 065 @Override 066 public void service(ServiceManager manager) throws ServiceException 067 { 068 super.service(manager); 069 _tagProviderEP = (TagProviderExtensionPoint) manager.lookup(TagProviderExtensionPoint.ROLE); 070 _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE); 071 } 072 073 @Override 074 public MetadataType getType() 075 { 076 return MetadataType.STRING; 077 } 078 079 @Override 080 public boolean isMultiple() 081 { 082 return true; 083 } 084 085 @Override 086 public boolean isSortable() 087 { 088 return false; 089 } 090 091 @SuppressWarnings("unchecked") 092 @Override 093 public Query getQuery(Object value, Operator operator, String language, Map<String, Object> contextualParameters) 094 { 095 if (value instanceof Map<?, ?>) 096 { 097 // Handle autoposting 098 Map<String, Object> valueAsMap = (Map<String, Object>) value; 099 100 String[] tagNames = parseStringArray(valueAsMap.get("value")); 101 102 boolean autoposting = BooleanUtils.isTrue((Boolean) valueAsMap.get("autoposting")); 103 return new TagQuery(operator, autoposting, tagNames); 104 } 105 else 106 { 107 return new TagQuery(operator, parseStringArray(value)); 108 } 109 } 110 111 @Override 112 public SearchField getSearchField() 113 { 114 return new TagSearchField(); 115 } 116 117 @Override 118 public String getWidget() 119 { 120 return "edition.tag"; 121 } 122 123 @Override 124 public Map<String, I18nizableText> getWidgetParameters(Configuration configuration) 125 { 126 Map<String, I18nizableText> params = new HashMap<>(); 127 128 params.put("targetType", new I18nizableText("CONTENT")); 129 params.put("allowToggleAutoposting", new I18nizableText("true")); 130 131 return params; 132 } 133 134 @Override 135 public String getRenderer() 136 { 137 return "Ametys.plugins.cms.search.SearchGridHelper.renderTag"; 138 } 139 140 @Override 141 public String getConverter() 142 { 143 return "Ametys.plugins.cms.search.SearchGridHelper.convertTag"; 144 } 145 146 @Override 147 public void index(Content content, SolrInputDocument document) 148 { 149 Set<String> tagsWithAncestors = new HashSet<>(); 150 String language = content.getLanguage(); 151 152 for (String tagName : content.getTags()) 153 { 154 document.addField(SolrFieldNames.TAGS, tagName); 155 156 if (!tagsWithAncestors.contains(tagName)) 157 { 158 document.addField(SolrFieldNames.ALL_TAGS, tagName); 159 tagsWithAncestors.add(tagName); 160 } 161 162 Tag tag = _tagProviderEP.getTag(tagName, getTagContextualParameters(content)); 163 if (tag != null) 164 { 165 String title = _i18nUtils.translate(tag.getTitle(), language); 166 SolrContentIndexer.indexFulltextValue(document, title, language); 167 } 168 169 // Index ancestors (descendant autoposting) 170 Set<Tag> ancestors = TagHelper.getAncestors(tag, false); 171 172 for (Tag ancestor : ancestors) 173 { 174 String ancestorName = ancestor.getName(); 175 if (!tagsWithAncestors.contains(ancestorName)) 176 { 177 document.addField(SolrFieldNames.ALL_TAGS, ancestorName); 178 179 // Plain text + autocompletion on ancestors (if enabled) 180 if (isAutopostingEnabled(content)) 181 { 182 String title = _i18nUtils.translate(ancestor.getTitle(), language); 183 SolrContentIndexer.indexFulltextValue(document, title, language); 184 } 185 186 tagsWithAncestors.add(tagName); 187 } 188 } 189 } 190 } 191 192 @Override 193 public Object getValue(Content content) 194 { 195 Set<String> tags = content.getTags(); 196 return tags.toArray(new String[tags.size()]); 197 } 198 199 @Override 200 public Object getJsonValue(Content content, boolean full) 201 { 202 String[] tags = (String[]) getValue(content); 203 if (full) 204 { 205 List<Map<String, Object>> fullValue = new ArrayList<>(); 206 207 Map<String, Object> params = getTagContextualParameters(content); 208 209 for (String tagName : tags) 210 { 211 Map<String, Object> infos = new HashMap<>(); 212 infos.put("name", tagName); 213 214 Tag tag = _tagProviderEP.getTag(tagName, params); 215 if (tag != null) 216 { 217 infos.put("label", tag.getTitle()); 218 } 219 220 fullValue.add(infos); 221 } 222 223 return fullValue; 224 } 225 226 return tags; 227 } 228 229 @Override 230 public void saxValue(ContentHandler handler, Content content) throws SAXException 231 { 232 String[] tags = (String[]) getValue(content); 233 234 XMLUtils.startElement(handler, getId()); 235 236 for (String tag : tags) 237 { 238 XMLUtils.createElement(handler, "tag", tag); 239 } 240 241 XMLUtils.endElement(handler, getId()); 242 } 243 244 /** 245 * Get the tags contextual parameters. 246 * @param content The content. 247 * @return the tags contextual parameters. 248 */ 249 protected Map<String, Object> getTagContextualParameters(Content content) 250 { 251 return Collections.emptyMap(); 252 } 253 254 /** 255 * Test if autoposting is enabled. 256 * @param content The content. 257 * @return true if enabled. 258 */ 259 protected boolean isAutopostingEnabled(Content content) 260 { 261 return true; 262 } 263 264 @Override 265 public Collection<SchemaDefinition> getSchemaDefinitions() 266 { 267 Collection<SchemaDefinition> definitions = super.getSchemaDefinitions(); 268 269 // Add allTags solr field 270 definitions.add(new FieldDefinition(SolrFieldNames.ALL_TAGS, SchemaHelper.getSchemaType(getType()), isMultiple(), false)); 271 return definitions; 272 } 273}