001/* 002 * Copyright 2016 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.userdirectory.page; 017 018import java.util.HashSet; 019import java.util.Iterator; 020import java.util.Set; 021 022import org.apache.avalon.framework.activity.Initializable; 023import org.apache.avalon.framework.component.Component; 024import org.apache.avalon.framework.context.Context; 025import org.apache.avalon.framework.context.ContextException; 026import org.apache.avalon.framework.context.Contextualizable; 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.cocoon.components.ContextHelper; 032import org.apache.cocoon.environment.Request; 033import org.apache.commons.lang.StringUtils; 034 035import org.ametys.cms.languages.LanguagesManager; 036import org.ametys.cms.repository.Content; 037import org.ametys.core.cache.AbstractCacheManager; 038import org.ametys.core.cache.Cache; 039import org.ametys.plugins.core.impl.cache.AbstractCacheKey; 040import org.ametys.plugins.repository.AmetysObjectResolver; 041import org.ametys.plugins.repository.RepositoryConstants; 042import org.ametys.plugins.repository.UnknownAmetysObjectException; 043import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 044import org.ametys.plugins.userdirectory.UserDirectoryPageHandler; 045import org.ametys.runtime.i18n.I18nizableText; 046import org.ametys.web.repository.page.Page; 047 048/** 049 * Resolves an user directory page path from the associated user content. 050 */ 051public class UserDirectoryPageResolver extends AbstractLogEnabled implements Component, Serviceable, Initializable, Contextualizable 052{ 053 /** The avalon role. */ 054 public static final String ROLE = UserDirectoryPageResolver.class.getName(); 055 056 /** The key cache for empty site */ 057 protected static final String _CACHE_KEY_EMPTY_SITE = "_ametys_no_site"; 058 059 /** The ametys object resolver. */ 060 protected AmetysObjectResolver _ametysResolver; 061 /** The user directory page handler */ 062 protected UserDirectoryPageHandler _pageHandler; 063 /** The cache manager */ 064 protected AbstractCacheManager _cacheManager; 065 /** The context */ 066 protected Context _context; 067 /** The languages manager */ 068 protected LanguagesManager _languagesManager; 069 070 static class UserPageElementKey extends AbstractCacheKey 071 { 072 UserPageElementKey(String lang, String contentId, String siteName, String contentTypeId) 073 { 074 super(lang, contentId, siteName, contentTypeId); 075 } 076 077 static UserPageElementKey of(String lang, String contentId, String siteName, String contentTypeId) 078 { 079 return new UserPageElementKey(lang, contentId, siteName, contentTypeId); 080 } 081 082 static UserPageElementKey of(String lang) 083 { 084 return new UserPageElementKey(lang, null, null, null); 085 } 086 } 087 088 @Override 089 public void service(ServiceManager serviceManager) throws ServiceException 090 { 091 _ametysResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 092 _pageHandler = (UserDirectoryPageHandler) serviceManager.lookup(UserDirectoryPageHandler.ROLE); 093 _cacheManager = (AbstractCacheManager) serviceManager.lookup(AbstractCacheManager.ROLE); 094 _languagesManager = (LanguagesManager) serviceManager.lookup(LanguagesManager.ROLE); 095 } 096 097 @Override 098 public void initialize() throws Exception 099 { 100 _cacheManager.createMemoryCache(ROLE, 101 new I18nizableText("plugin.user-directory", "PLUGINS_USER_DIRECTORY_USER_PAGE_CACHE_LABEL"), 102 new I18nizableText("plugin.user-directory", "PLUGINS_USER_DIRECTORY_USER_PAGE_CACHE_DESCRIPTION"), 103 true, 104 null); 105 } 106 107 public void contextualize(Context context) throws ContextException 108 { 109 _context = context; 110 } 111 112 /** 113 * Return the user page 114 * @param userContent the user page content 115 * @param siteName The current site name. 116 * @param sitemapName The current sitemap name. 117 * @return the user page or <code>null</code> if the user page does not exist 118 */ 119 public UserPage getUserPage(Content userContent, String siteName, String sitemapName) 120 { 121 for (String contentTypeId : userContent.getTypes()) 122 { 123 UserPage userPage = getUserPage(userContent, siteName, sitemapName, contentTypeId); 124 if (userPage != null) 125 { 126 return userPage; 127 } 128 } 129 130 return null; 131 } 132 133 /** 134 * Return the user page 135 * @param userContent the user page content 136 * @param siteName The current site name. 137 * @param sitemapName The current sitemap name. 138 * @param contentTypeId the content type id 139 * @return the user page or <code>null</code> if the user page does not exist 140 */ 141 public UserPage getUserPage(Content userContent, String siteName, String sitemapName, String contentTypeId) 142 { 143 String siteKey = StringUtils.isNotBlank(siteName) ? siteName : _CACHE_KEY_EMPTY_SITE; 144 String pageId = null; 145 String contentLang = userContent.getLanguage(); 146 if (StringUtils.isNotBlank(contentLang)) 147 { 148 // Just get the user page of content language 149 UserPageElementKey key = UserPageElementKey.of(contentLang, userContent.getId(), siteKey, contentTypeId); 150 pageId = _getCache().get(key, k -> _getUserPageId(userContent, siteName, contentLang, contentTypeId)); 151 } 152 else 153 { 154 // The content is multilingual, so test different languages starting with the sitemap name to get an user content page 155 Iterator<String> languageIterator = _getLanguagesToTest(sitemapName).iterator(); 156 while (pageId == null && languageIterator.hasNext()) 157 { 158 String lang = languageIterator.next(); 159 UserPageElementKey key = UserPageElementKey.of(lang, userContent.getId(), siteKey, contentTypeId); 160 pageId = _getCache().get(key, k -> _getUserPageId(userContent, siteName, lang, contentTypeId)); 161 } 162 } 163 164 return _getUserPage(pageId); 165 } 166 167 private Set<String> _getLanguagesToTest(String givenLang) 168 { 169 Set<String> languages = new HashSet<>(); 170 171 // First, test the given language if not blank 172 if (StringUtils.isNotBlank(givenLang)) 173 { 174 languages.add(givenLang); 175 } 176 177 // Then test english language 178 languages.add("en"); 179 180 // Finaly test other languages 181 languages.addAll(_languagesManager.getAvailableLanguages().keySet()); 182 183 return languages; 184 } 185 186 /** 187 * Return the user page id 188 * @param userContent the user content 189 * @param siteName the site name 190 * @param language the given language 191 * @param contentTypeId the content type id 192 * @return the user page id or <code>null</code> if the user page does not exist 193 */ 194 protected String _getUserPageId(Content userContent, String siteName, String language, String contentTypeId) 195 { 196 Request request = ContextHelper.getRequest(_context); 197 198 // Retrieve current workspace 199 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 200 201 try 202 { 203 // Use default workspace 204 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, RepositoryConstants.DEFAULT_WORKSPACE); 205 206 Page userDirectoryRootPage = _pageHandler.getUserDirectoryRootPage(siteName, language, contentTypeId); 207 if (userDirectoryRootPage == null) 208 { 209 return null; 210 } 211 212 return getUserPageId(userDirectoryRootPage, userContent); 213 } 214 finally 215 { 216 // Restore context 217 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 218 } 219 } 220 221 /** 222 * Return the user page 223 * @param userDirectoryRootPage the user directory root page 224 * @param userContent the user content 225 * @return the user page or <code>null</code> if the user page does not exist 226 */ 227 public UserPage getUserPage(Page userDirectoryRootPage, Content userContent) 228 { 229 String pageId = getUserPageId(userDirectoryRootPage, userContent); 230 return _getUserPage(pageId); 231 } 232 233 /** 234 * Return the user page identifier 235 * @param userDirectoryRootPage the user directory root page 236 * @param userContent the user content 237 * @return the user page or <code>null</code> if the user page does not exist 238 */ 239 public String getUserPageId(Page userDirectoryRootPage, Content userContent) 240 { 241 // E.g: uduser://path?rootId=...&contentId=... 242 String userPath = _getUserPath(userDirectoryRootPage, userContent); 243 if (userPath != null) 244 { 245 return UserPage.getId(userPath, userDirectoryRootPage.getId(), userContent.getId()); 246 } 247 return null; 248 } 249 250 /** 251 * Get the path of a user content 252 * @param userDirectoryRootPage The user directory root page where to look into 253 * @param userContent The user content 254 * @return The path or <code>null</code> if the user page does not exist 255 */ 256 private String _getUserPath(Page userDirectoryRootPage, Content userContent) 257 { 258 String value = _pageHandler.getTransformedClassificationValue(userDirectoryRootPage, userContent); 259 return value != null ? value.replaceAll("(.)(?!$)", "$1/") : null; 260 } 261 262 private UserPage _getUserPage(String pageId) 263 { 264 try 265 { 266 return pageId != null ? _ametysResolver.resolveById(pageId) : null; 267 } 268 catch (UnknownAmetysObjectException e) 269 { 270 return null; 271 } 272 } 273 274 private Cache<UserPageElementKey, String> _getCache() 275 { 276 return _cacheManager.get(ROLE); 277 } 278 279 /** 280 * Invalidate the user page cache for one language. If lang is <code>null</code>, invalidate all 281 * @param lang the lang. Can be <code>null</code> 282 */ 283 public void invalidateUserPageCache(String lang) 284 { 285 if (StringUtils.isNotBlank(lang)) 286 { 287 _getCache().invalidate(UserPageElementKey.of(lang)); 288 } 289 else 290 { 291 _getCache().invalidateAll(); 292 } 293 } 294}