001/* 002 * Copyright 2019 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 */ 016 017package org.ametys.web.site; 018 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.Map.Entry; 024import java.util.Set; 025 026import org.apache.avalon.framework.component.Component; 027import org.apache.avalon.framework.logger.AbstractLogEnabled; 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.avalon.framework.service.Serviceable; 031import org.apache.commons.lang3.StringUtils; 032 033import org.ametys.cms.tag.CMSTag; 034import org.ametys.cms.tag.CMSTag.TagVisibility; 035import org.ametys.cms.tag.Tag; 036import org.ametys.cms.tag.TagsDAO; 037import org.ametys.core.right.RightManager; 038import org.ametys.core.ui.Callable; 039import org.ametys.core.user.CurrentUserProvider; 040import org.ametys.core.user.UserIdentity; 041import org.ametys.core.util.I18nUtils; 042import org.ametys.plugins.repository.AmetysObject; 043import org.ametys.plugins.repository.AmetysObjectResolver; 044import org.ametys.web.repository.page.Page; 045import org.ametys.web.repository.page.PageDAO; 046import org.ametys.web.repository.sitemap.Sitemap; 047 048/** 049 * Component to help AddPageWizard to get all it's infos without calling {@link PageDAO} or {@link TagsDAO} multiple times 050 */ 051public class AddPageWizardHelper extends AbstractLogEnabled implements Serviceable, Component 052{ 053 /** Avalon Role */ 054 public static final String ROLE = AddPageWizardHelper.class.getName(); 055 056 /** Ametys Object Resolver */ 057 protected AmetysObjectResolver _resolver; 058 059 /** Right Manager */ 060 protected RightManager _rightManager; 061 062 /** Current User Provider */ 063 protected CurrentUserProvider _currentUserProvider; 064 065 /** Tags DAO */ 066 protected TagsDAO _tagsDAO; 067 068 /** The I18n utils */ 069 protected I18nUtils _i18nUtils; 070 071 public void service(ServiceManager manager) throws ServiceException 072 { 073 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 074 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 075 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 076 _tagsDAO = (TagsDAO) manager.lookup(TagsDAO.ROLE); 077 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 078 } 079 080 /** 081 * Get the page's properties 082 * @param pageId the page ID 083 * @param depth number of parents to fetch to generate the title (0 to display only the page name) 084 * @param separator separator between the parents (e.g. " > " to have "grandParent > parent > page") 085 * @return the properties 086 */ 087 @Callable 088 public Map<String, Object> getPageInfos (String pageId, int depth, String separator) 089 { 090 AmetysObject page = _resolver.resolveById(pageId); 091 UserIdentity user = _currentUserProvider.getUser(); 092 return getPageDetails(page, user, depth, separator); 093 } 094 095 /** 096 * Get the page's properties 097 * @param pageIds the page IDs 098 * @param depth number of parents to fetch to generate the title (0 to display only the page name) 099 * @param separator separator between the parents (e.g. " > " to have "grandParent > parent > page") 100 * @return the properties 101 */ 102 @Callable 103 public List<Map<String, Object>> getPagesInfos (List<String> pageIds, int depth, String separator) 104 { 105 UserIdentity user = _currentUserProvider.getUser(); 106 List<Map<String, Object>> result = new ArrayList<>(); 107 for (String pageId : pageIds) 108 { 109 AmetysObject page = _resolver.resolveById(pageId); 110 Map<String, Object> pageInfos = getPageDetails(page, user, depth, separator); 111 result.add(pageInfos); 112 } 113 return result; 114 } 115 116 /** 117 * get the id, rights and formatted title of a page 118 * @param page page/sitemap to work on 119 * @param user user to check rights 120 * @param depth number of parents to fetch to generate the title (0 to display only the page name) 121 * @param separator separator between the parents (e.g. " > " to have "grandParent > parent > page") 122 * @return a map with id (String), rights (Set<String>) and title (String) 123 */ 124 protected Map<String, Object> getPageDetails(AmetysObject page, UserIdentity user, int depth, String separator) 125 { 126 Map<String, Object> result = new HashMap<>(); 127 result.put("id", page.getId()); 128 Set<String> userRights = _rightManager.getUserRights(user, page); 129 result.put("rights", userRights); 130 131 String title = ""; 132 AmetysObject parent = page; 133 for (int i = 0; i <= depth; i++) 134 { 135 // We stop if we have no parent 136 // OR parent is not a page 137 // AND parent is not a Sitemap 138 // OR parent is a Sitemap and i != 0 (we display the sitemap only if it is specifically requested) 139 if (parent == null 140 || !(parent instanceof Page || parent instanceof Sitemap) 141 || (parent instanceof Sitemap && i != 0)) 142 { 143 // If we have reached the sitemap, we stop to try to get a parent 144 break; 145 } 146 147 if (i != 0) 148 { 149 title = separator + title; 150 } 151 152 if (parent instanceof Page) 153 { 154 title = ((Page) parent).getTitle() + title; 155 } 156 else if (parent instanceof Sitemap) 157 { 158 Sitemap sitemap = (Sitemap) parent; 159 title = sitemap.getSiteName() + " (" + sitemap.getSitemapName() + ")" + title; 160 } 161 parent = parent.getParent(); 162 } 163 164 result.put("title", title); 165 166 return result; 167 } 168 169 /** 170 * Get the list of sub-tags for a list of input tags 171 * @param tagNames name of input tags where we want to get the list of contained tags 172 * @param filters list of filters to disable, can contain PAGE_PRIVATE, CONTENT_PRIVATE, PAGE, CONTENT 173 * @param targetType tag target type 174 * @param showCategories true to display tags containing other tags. If false, only the non-parents tags will be listed 175 * @param separator separator between the parents (e.g. " > " to have "grandParent > parent > page") 176 * @param contextualParameters the contextual parameters 177 * @return a list with one item per requested tagName, each one containing name (String), title (String) and tags (List of Map<String, String>, each map containing a name and title) 178 */ 179 @Callable 180 public List<Map<String, Object>> getTags(List<String> tagNames, List<String> filters, String targetType, boolean showCategories, String separator, Map<String, Object> contextualParameters) 181 { 182 List<Map<String, Object>> result = new ArrayList<>(); 183 for (String tagName : tagNames) 184 { 185 Tag tag = _tagsDAO.getTag(tagName, contextualParameters); 186 if (tag != null) 187 { 188 if (!isTagAvailable(tag, filters, targetType)) 189 { 190 continue; 191 } 192 Map<String, Object> tagMap = parseTag(tag, tag, separator); 193 List<Map<String, Object>> subTags = getSubTags(tag, tag, filters, targetType, showCategories, separator); 194 tagMap.put("tags", subTags); 195 result.add(tagMap); 196 } 197 else 198 { 199 getLogger().warn("The tag '" + tagName + "' can not be found."); 200 } 201 } 202 203 return result; 204 } 205 206 /** 207 * Check if we can use this tag 208 * @param tag tag to check 209 * @param filters list of filters to disable, can contain PAGE_PRIVATE, CONTENT_PRIVATE, PAGE, CONTENT 210 * @param targetType tag target type 211 * @return true if this tag is not impacted by the input filters 212 */ 213 protected boolean isTagAvailable(Tag tag, List<String> filters, String targetType) 214 { 215 // If we can find the visibility/target of the tag, we filter it 216 if (tag instanceof CMSTag) 217 { 218 CMSTag cmsTag = (CMSTag) tag; 219 220 //PAGE_PRIVATE, CONTENT_PRIVATE, PAGE, CONTENT 221 boolean filterContent = filters.contains("CONTENT"); 222 boolean filterPage = filters.contains("PAGE"); 223 boolean filterPrivateContent = filters.contains("CONTENT_PRIVATE"); 224 boolean filterPrivatePage = filters.contains("PAGE_PRIVATE"); 225 226 boolean isContent = cmsTag.getTarget().getName().equals("CONTENT"); 227 boolean isPage = cmsTag.getTarget().getName().equals("PAGE"); 228 boolean isPrivate = cmsTag.getVisibility() == TagVisibility.PRIVATE; 229 230 boolean targetTypeOK = StringUtils.isEmpty(targetType) || cmsTag.getTarget().getName().equals(targetType); 231 boolean typeContentOK = targetTypeOK || (isContent && !(filterContent || filterPrivateContent)); 232 boolean typePageOK = targetTypeOK || (isPage && !(filterPage || filterPrivatePage)); 233 boolean visibilityOK = !(isPrivate && (filterPrivateContent || filterPrivatePage)); 234 235 return typeContentOK && typePageOK && visibilityOK; 236// return !((filterContent && isContent) 237// || (filterPage && isPage) 238// || (filterPrivateContent && isContent && isPrivate) 239// || (filterPrivatePage && isPage && isPrivate)); 240 } 241 return true; 242 } 243 244 /** 245 * Iterate over all sub-tags for a tag and list them with their full name 246 * @param tag tag to iterate over 247 * @param rootTag needed to display the hierarchy in the full name 248 * @param filters list of filters to disable, can contain PAGE_PRIVATE, CONTENT_PRIVATE, PAGE, CONTENT 249 * @param targetType tag target type 250 * @param showCategories true to display tags containing other tags. If false, only the non-parents tags will be listed 251 * @param separator separator between the parents (e.g. " > " to have "grandParent > parent > page") 252 * @return a list with one item per requested tagName, each one containing name (String), title (String) and tags (List of Map<String, String>, each map containing a name and title) 253 */ 254 private List<Map<String, Object>> getSubTags(Tag tag, Tag rootTag, List<String> filters, String targetType, boolean showCategories, String separator) 255 { 256 List<Map<String, Object>> subTags = new ArrayList<>(); 257 Map<String, ? extends Tag> tags = tag.getTags(); 258 259 for (Entry<String, ? extends Tag> entry : tags.entrySet()) 260 { 261 Tag subTag = entry.getValue(); 262 if (!isTagAvailable(subTag, filters, targetType)) 263 { 264 break; 265 } 266 Map<String, Object> subTagMap = parseTag(subTag, rootTag, separator); 267 if (showCategories || subTag.getTags().isEmpty()) 268 { 269 subTags.add(subTagMap); 270 } 271 subTags.addAll(getSubTags(subTag, rootTag, filters, targetType, showCategories, separator)); 272 } 273 return subTags; 274 } 275 private Map<String, Object> parseTag(Tag tag, Tag rootTag, String separator) 276 { 277 Map<String, Object> result = new HashMap<>(); 278 279 result.put("name", tag.getName()); 280 result.put("title", getTagFullName(tag, rootTag, separator)); 281 282 return result; 283 } 284 285 /** 286 * Get the formatted tag name for a tag, with it's parents names 287 * @param tag tag to find the name 288 * @param rootTag root tag to display full name until this tag 289 * @param separator separator between the parents (e.g. " > " to have "grandParent > parent > child") 290 * @return a formatted name 291 */ 292 protected String getTagFullName(Tag tag, Tag rootTag, String separator) 293 { 294 String title = _i18nUtils.translate(tag.getTitle()); 295 Tag parent = tag.getParent(); 296 String rootTagId = rootTag.getId(); 297 298 while (parent != null && !parent.getId().equals(rootTagId)) 299 { 300 String tagTitle = _i18nUtils.translate(parent.getTitle()); 301 title = tagTitle + separator + title; 302 parent = parent.getParent(); 303 } 304 return title; 305 } 306}