001/* 002 * Copyright 2018 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.plugins.ugc.page; 017 018import java.util.HashMap; 019import java.util.Locale; 020import java.util.Map; 021import java.util.Set; 022import java.util.stream.Collectors; 023 024import org.apache.avalon.framework.component.Component; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.avalon.framework.service.Serviceable; 028import org.apache.commons.lang.StringUtils; 029 030import org.ametys.cms.contenttype.ContentType; 031import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 032import org.ametys.cms.contenttype.MetadataDefinition; 033import org.ametys.cms.contenttype.MetadataType; 034import org.ametys.cms.repository.Content; 035import org.ametys.cms.repository.ContentTypeExpression; 036import org.ametys.cms.repository.LanguageExpression; 037import org.ametys.core.util.I18nUtils; 038import org.ametys.plugins.repository.AmetysObjectIterable; 039import org.ametys.plugins.repository.AmetysObjectResolver; 040import org.ametys.plugins.repository.AmetysRepositoryException; 041import org.ametys.plugins.repository.query.QueryHelper; 042import org.ametys.plugins.repository.query.SortCriteria; 043import org.ametys.plugins.repository.query.expression.AndExpression; 044import org.ametys.plugins.repository.query.expression.Expression; 045import org.ametys.plugins.repository.query.expression.Expression.Operator; 046import org.ametys.plugins.repository.query.expression.StringExpression; 047import org.ametys.plugins.repository.query.expression.VirtualFactoryExpression; 048import org.ametys.runtime.i18n.I18nizableText; 049import org.ametys.runtime.parameter.Enumerator; 050import org.ametys.runtime.plugin.component.AbstractLogEnabled; 051import org.ametys.web.repository.page.Page; 052import org.ametys.web.repository.page.PageQueryHelper; 053 054/** 055 * Component providing methods to retrieve ugc virtual pages, such as the ugc root, 056 * transitional page and ugc content page. 057 */ 058public class UGCPageHandler extends AbstractLogEnabled implements Component, Serviceable 059{ 060 /** The attribute to get the name of transitional page */ 061 public static final String ATTRIBUTE_TRANSITIONAL_PAGE_METADATA_VALUE = "metadata_value"; 062 063 /** The attribute to get the title of transitional page */ 064 public static final String ATTRIBUTE_TRANSITIONAL_PAGE_TITLE = "title"; 065 066 /** The avalon role. */ 067 public static final String ROLE = UGCPageHandler.class.getName(); 068 069 /** The metadata name for the content type of the ugc */ 070 public static final String CONTENT_TYPE_METADATA_NAME = "ugc-root-contenttype"; 071 072 /** The metadata name for the classification metadata of the ugc */ 073 public static final String CLASSIFICATION_METADATA_METADATA_NAME = "ugc-root-classification-metadata"; 074 075 /** The ametys object resolver */ 076 protected AmetysObjectResolver _resolver; 077 078 /** The content type extension point */ 079 protected ContentTypeExtensionPoint _cTypeEP; 080 081 /** The i18n utils */ 082 protected I18nUtils _i18nUtils; 083 084 @Override 085 public void service(ServiceManager manager) throws ServiceException 086 { 087 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 088 _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 089 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 090 } 091 092 /** 093 * Gets the name of the classification metadata 094 * @param rootPage The ugc root page 095 * @return the name of the classification metadata 096 */ 097 public String getClassificationMetadata(Page rootPage) 098 { 099 return rootPage.getValue(CLASSIFICATION_METADATA_METADATA_NAME); 100 } 101 102 /** 103 * Gets the content type id 104 * @param rootPage The ugc root page 105 * @return the content type id 106 */ 107 public String getContentTypeId(Page rootPage) 108 { 109 return rootPage.getValue(CONTENT_TYPE_METADATA_NAME); 110 } 111 112 /** 113 * Gets the ugc root pages from the given content type id. 114 * @param siteName the site name 115 * @param sitemapName the sitemap name 116 * @param contentTypeId The content type id 117 * @return the ugc root page. 118 * @throws AmetysRepositoryException if an error occured. 119 */ 120 public Page getUGCRootPage(String siteName, String sitemapName, String contentTypeId) throws AmetysRepositoryException 121 { 122 Expression expression = new VirtualFactoryExpression(VirtualUGCPageFactory.class.getName()); 123 Expression contentTypeExp = new StringExpression(CONTENT_TYPE_METADATA_NAME, Operator.EQ, contentTypeId); 124 125 AndExpression andExp = new AndExpression(expression, contentTypeExp); 126 127 String query = PageQueryHelper.getPageXPathQuery(siteName, sitemapName, null, andExp, null); 128 129 AmetysObjectIterable<Page> pages = _resolver.query(query); 130 131 return pages.iterator().hasNext() ? pages.iterator().next() : null; 132 } 133 134 /** 135 * Get the ugc root pages 136 * @param siteName the current site. 137 * @param sitemapName the sitemap name. 138 * @return the ugc root pages 139 * @throws AmetysRepositoryException if an error occured. 140 */ 141 public Set<Page> getUGCRootPages(String siteName, String sitemapName) throws AmetysRepositoryException 142 { 143 Expression expression = new VirtualFactoryExpression(VirtualUGCPageFactory.class.getName()); 144 145 String query = PageQueryHelper.getPageXPathQuery(siteName, sitemapName, null, expression, null); 146 147 AmetysObjectIterable<Page> pages = _resolver.query(query); 148 149 return pages.stream().collect(Collectors.toSet()); 150 } 151 152 /** 153 * Get orgUnit contents from rootPage 154 * @param rootPage the root page 155 * @return the list of orgUnit contents 156 */ 157 public AmetysObjectIterable<Content> getContentsForRootPage(Page rootPage) 158 { 159 String lang = rootPage.getSitemapName(); 160 String contentType = getContentTypeId(rootPage); 161 162 ContentTypeExpression contentTypeExp = new ContentTypeExpression(Operator.EQ, contentType); 163 164 Expression finalExpr = new AndExpression(contentTypeExp, new LanguageExpression(Operator.EQ, lang)); 165 166 SortCriteria sort = new SortCriteria(); 167 sort.addCriterion(Content.ATTRIBUTE_TITLE, true, true); 168 169 String xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", finalExpr, sort); 170 171 return _resolver.query(xPathQuery); 172 } 173 174 /** 175 * Get the map of transitional page (name : (id, title)) 176 * @param rootPage the root page 177 * @return The map of transitional page 178 */ 179 public Map<String, Map<String, String>> getTransitionalPage(Page rootPage) 180 { 181 Map<String, Map<String, String>> transitionalPage = new HashMap<>(); 182 String classificationMetadataPath = getClassificationMetadata(rootPage); 183 if (StringUtils.isBlank(classificationMetadataPath)) 184 { 185 // No classification metadata defined, so no transitional page 186 return transitionalPage; 187 } 188 189 String contentTypeId = getContentTypeId(rootPage); 190 ContentType contentType = _cTypeEP.getExtension(contentTypeId); 191 192 MetadataDefinition metadataDefinition = contentType.getMetadataDefinitionByPath(classificationMetadataPath); 193 if (metadataDefinition != null) 194 { 195 if (metadataDefinition.getType().equals(MetadataType.CONTENT)) 196 { 197 String metadataContentType = metadataDefinition.getContentType(); 198 AmetysObjectIterable<Content> allContents = _getAllContents(rootPage, metadataContentType); 199 for (Content content : allContents) 200 { 201 Map<String, String> attributes = new HashMap<>(); 202 attributes.put(ATTRIBUTE_TRANSITIONAL_PAGE_METADATA_VALUE, content.getId()); 203 attributes.put(ATTRIBUTE_TRANSITIONAL_PAGE_TITLE, content.getTitle(new Locale(rootPage.getSitemapName()))); 204 205 transitionalPage.put(content.getName(), attributes); 206 } 207 } 208 else if (metadataDefinition.getEnumerator() != null) 209 { 210 Enumerator enumerator = metadataDefinition.getEnumerator(); 211 try 212 { 213 Map<Object, I18nizableText> entries = enumerator.getEntries(); 214 for (Object key : entries.keySet()) 215 { 216 I18nizableText text = entries.get(key); 217 218 Map<String, String> attributes = new HashMap<>(); 219 attributes.put(ATTRIBUTE_TRANSITIONAL_PAGE_METADATA_VALUE, (String) key); 220 attributes.put(ATTRIBUTE_TRANSITIONAL_PAGE_TITLE, _i18nUtils.translate(text)); 221 222 transitionalPage.put((String) key, attributes); 223 } 224 } 225 catch (Exception e) 226 { 227 getLogger().error("An error occurred. Can't get enumerator entries for metadata path " + classificationMetadataPath, e); 228 } 229 } 230 } 231 232 return transitionalPage; 233 } 234 235 /** 236 * Get contents under transitional page 237 * @param rootPage the root page 238 * @param metadataValue the metadata value (linked to the transitional page) 239 * @return list of contents under transitional page 240 */ 241 public AmetysObjectIterable<Content> getContentsForTransitionalPage(Page rootPage, String metadataValue) 242 { 243 String classificationMetadata = getClassificationMetadata(rootPage); 244 245 String lang = rootPage.getSitemapName(); 246 String contentType = getContentTypeId(rootPage); 247 248 ContentTypeExpression contentTypeExp = new ContentTypeExpression(Operator.EQ, contentType); 249 StringExpression metadataExpression = new StringExpression(classificationMetadata, Operator.EQ, metadataValue); 250 251 Expression finalExpr = new AndExpression(contentTypeExp, metadataExpression, new LanguageExpression(Operator.EQ, lang)); 252 253 SortCriteria sort = new SortCriteria(); 254 sort.addCriterion(Content.ATTRIBUTE_TITLE, true, true); 255 256 String xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", finalExpr, sort); 257 258 return _resolver.query(xPathQuery); 259 } 260 261 /** 262 * Get all content with the content type id 263 * @param rootPage the root page 264 * @param contentTypeId the content type id 265 * @return the list of contents 266 */ 267 protected AmetysObjectIterable<Content> _getAllContents(Page rootPage, String contentTypeId) 268 { 269 String xPathQuery = null; 270 ContentTypeExpression contentTypeExp = new ContentTypeExpression(Operator.EQ, contentTypeId); 271 272 ContentType contentType = _cTypeEP.getExtension(contentTypeId); 273 if (contentType.isReferenceTable()) 274 { 275 if (contentType.isMultilingual()) 276 { 277 xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", contentTypeExp, null); 278 } 279 else 280 { 281 String lang = rootPage.getSitemapName(); 282 Expression finalExpr = new AndExpression(contentTypeExp, new LanguageExpression(Operator.EQ, lang)); 283 284 xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", finalExpr, null); 285 } 286 } 287 else 288 { 289 SortCriteria sort = new SortCriteria(); 290 sort.addCriterion(Content.ATTRIBUTE_TITLE, true, true); 291 292 StringExpression siteEpx = new StringExpression("site", Operator.EQ, rootPage.getSiteName()); 293 String lang = rootPage.getSitemapName(); 294 Expression finalExpr = new AndExpression(contentTypeExp, siteEpx, new LanguageExpression(Operator.EQ, lang)); 295 296 xPathQuery = QueryHelper.getXPathQuery(null, "ametys:content", finalExpr, sort); 297 } 298 299 return _resolver.query(xPathQuery); 300 } 301}