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.content.referencetable; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022 023import org.apache.avalon.framework.parameters.Parameters; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.cocoon.acting.ServiceableAction; 027import org.apache.cocoon.environment.ObjectModelHelper; 028import org.apache.cocoon.environment.Redirector; 029import org.apache.cocoon.environment.Request; 030import org.apache.cocoon.environment.SourceResolver; 031 032import org.ametys.cms.content.ContentHelper; 033import org.ametys.cms.contenttype.ContentType; 034import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 035import org.ametys.cms.contenttype.ContentTypesHelper; 036import org.ametys.cms.contenttype.MetadataDefinition; 037import org.ametys.cms.repository.Content; 038import org.ametys.cms.repository.ContentQueryHelper; 039import org.ametys.cms.repository.ContentTypeExpression; 040import org.ametys.core.cocoon.JSonReader; 041import org.ametys.plugins.repository.AmetysObjectIterable; 042import org.ametys.plugins.repository.AmetysObjectResolver; 043import org.ametys.plugins.repository.EmptyIterable; 044import org.ametys.plugins.repository.query.SortCriteria; 045import org.ametys.plugins.repository.query.expression.AndExpression; 046import org.ametys.plugins.repository.query.expression.Expression; 047import org.ametys.plugins.repository.query.expression.Expression.Operator; 048import org.ametys.plugins.repository.query.expression.MetadataExpression; 049import org.ametys.plugins.repository.query.expression.NotExpression; 050import org.ametys.plugins.repository.query.expression.OrExpression; 051import org.ametys.plugins.repository.query.expression.StringExpression; 052 053/** 054 * Action for getting information about the hierarchy of a reference table 055 */ 056public class GetHierarchicalReferenceTablesAction extends ServiceableAction 057{ 058 /** The extension point for content types */ 059 protected ContentTypeExtensionPoint _contentTypeEP; 060 /** The resolver for Ametys Objects */ 061 protected AmetysObjectResolver _resolver; 062 /** The helper component for hierarchical reference tables */ 063 protected HierarchicalReferenceTablesHelper _hierarchicalReferenceTablesHelper; 064 /** Content types helper */ 065 protected ContentTypesHelper _cTypeHelper; 066 /** The content helper */ 067 protected ContentHelper _contentHelper; 068 069 @Override 070 public void service(ServiceManager smanager) throws ServiceException 071 { 072 super.service(smanager); 073 _contentTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE); 074 _contentHelper = (ContentHelper) smanager.lookup(ContentHelper.ROLE); 075 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 076 _hierarchicalReferenceTablesHelper = (HierarchicalReferenceTablesHelper) smanager.lookup(HierarchicalReferenceTablesHelper.ROLE); 077 _cTypeHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE); 078 } 079 080 @Override 081 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 082 { 083 Map<String, Object> result = new HashMap<>(); 084 Request request = ObjectModelHelper.getRequest(objectModel); 085 086 @SuppressWarnings("unchecked") 087 Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 088 089 String contentId = jsParameters != null ? (String) jsParameters.get("contentId") : request.getParameter("contentId"); 090 String leafContentTypeId = jsParameters != null ? (String) jsParameters.get("leafContentType") : request.getParameter("leafContentType"); 091 ContentType leafContentType = _contentTypeEP.getExtension(leafContentTypeId); 092 093 if (leafContentType == null) 094 { 095 getLogger().warn(String.format("Unknown content type id '%s'", leafContentTypeId)); 096 return EMPTY_MAP; 097 } 098 099 ContentType topLevelContentType = _hierarchicalReferenceTablesHelper.getTopLevelType(leafContentType); 100 101 try (AmetysObjectIterable<Content> contents = "root".equals(contentId) ? _getRootChildren(topLevelContentType) : _getChildContents(contentId)) 102 { 103 List<Map<String, Object>> children = new ArrayList<>(); 104 for (Content child : contents) 105 { 106 children.add(_contentToJson(child, false, topLevelContentType, leafContentType)); 107 } 108 result.put("children", children); 109 } 110 111 112 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 113 return EMPTY_MAP; 114 } 115 116 private AmetysObjectIterable<Content> _getRootChildren(ContentType rootContentType) 117 { 118 Expression expr = new ContentTypeExpression(Operator.EQ, rootContentType.getId()); 119 MetadataDefinition parentMetadata = rootContentType.getParentMetadata(); 120 121 if (parentMetadata != null && rootContentType.getId().equals(parentMetadata.getContentType())) 122 { 123 // even if it is the top level type, parentMetadata can be not null if it references itself as parent 124 expr = new AndExpression(expr, new NotExpression(new MetadataExpression(parentMetadata.getName()))); 125 } 126 127 //element(*, ametys:content)[@ametys-internal:contentType = 'foo.contentType' and not(@ametys:pointingMetadata)] 128 129 String query = ContentQueryHelper.getContentXPathQuery(expr); 130 return _resolver.query(query); 131 } 132 133 private AmetysObjectIterable<Content> _getChildContents(String contentId) 134 { 135 Content refTableEntry = _resolver.resolveById(contentId); 136 List<ContentType> childContentTypes = _hierarchicalReferenceTablesHelper.getChildContentTypes(refTableEntry); 137 138 if (!childContentTypes.isEmpty()) 139 { 140 List<Expression> exprs = new ArrayList<>(); 141 for (ContentType childContentType : childContentTypes) 142 { 143 MetadataDefinition pointingMetadata = childContentType.getParentMetadata(); 144 String pointingMetadataName = pointingMetadata.getName(); 145 146 // //element(*, ametys:content)[@ametys-internal:contentType = 'foo.child.contentType' and @ametys:pointingMetadata = 'contentId'] 147 Expression cTypeExpr = new ContentTypeExpression(Operator.EQ, childContentType.getId()); 148 Expression parentExpr = new StringExpression(pointingMetadataName, Operator.EQ, contentId); 149 150 exprs.add(new AndExpression(cTypeExpr, parentExpr)); 151 } 152 153 Expression finalExpr = new OrExpression(exprs.toArray(new Expression[exprs.size()])); 154 155 SortCriteria sortCriteria = new SortCriteria(); 156 sortCriteria.addCriterion("title", true, true); 157 String xPathQuery = ContentQueryHelper.getContentXPathQuery(finalExpr, sortCriteria); 158 159 return _resolver.query(xPathQuery); 160 } 161 162 return new EmptyIterable<>(); 163 } 164 165 private Map<String, Object> _contentToJson(Content content, boolean full, ContentType topContentType, ContentType leafContentType) 166 { 167 Map<String, Object> map = new HashMap<>(); 168 169 map.put("contentId", content.getId()); 170 map.put("contenttypesIds", content.getTypes()); 171 map.put("name", content.getName()); 172 map.put("title", _contentHelper.getTitle(content)); 173 map.put("lang", content.getLanguage()); 174 175 map.put("iconGlyph", _cTypeHelper.getIconGlyph(content)); 176 map.put("iconDecorator", _cTypeHelper.getIconDecorator(content)); 177 map.put("iconSmall", _cTypeHelper.getSmallIcon(content)); 178 map.put("iconMedium", _cTypeHelper.getMediumIcon(content)); 179 map.put("iconLarge", _cTypeHelper.getLargeIcon(content)); 180 181 map.put("canBeChecked", _cTypeHelper.isInstanceOf(content, leafContentType.getId())); 182 183 map.put("leaf", _isLeaf(content, topContentType.getId(), leafContentType.getId())); 184 185 AmetysObjectIterable<Content> children = _getChildContents(content.getId()); 186 if (children.getSize() == 0) 187 { 188 // No child 189 map.put("children", new ArrayList()); 190 } 191 else if (full) 192 { 193 List<Map<String, Object>> childrenInfos = new ArrayList<>(); 194 for (Content child : children) 195 { 196 childrenInfos.add(_contentToJson(child, false, topContentType, leafContentType)); 197 } 198 map.put("children", childrenInfos); 199 } 200 201 return map; 202 } 203 204 private boolean _isLeaf(Content content, String topContentTypeId, String leafContentTypeId) 205 { 206 if (_cTypeHelper.isInstanceOf(content, leafContentTypeId)) 207 { 208 // a child can be a leaf, or it can be a hierarchy with only one content type, referencing itself as parent 209 // hierarchy with only one content type, referencing itself as parent 210 return !topContentTypeId.equals(leafContentTypeId); 211 } 212 else 213 { 214 return false; 215 } 216 } 217}