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.web.repository.site; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Objects; 023import java.util.stream.Collectors; 024 025import org.apache.avalon.framework.parameters.Parameters; 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.cocoon.acting.ServiceableAction; 029import org.apache.cocoon.environment.ObjectModelHelper; 030import org.apache.cocoon.environment.Redirector; 031import org.apache.cocoon.environment.Request; 032import org.apache.cocoon.environment.SourceResolver; 033import org.apache.commons.lang.BooleanUtils; 034import org.apache.commons.lang.StringUtils; 035 036import org.ametys.cms.repository.Content; 037import org.ametys.core.cocoon.JSonReader; 038import org.ametys.core.group.GroupDirectoryContextHelper; 039import org.ametys.core.group.GroupDirectoryDAO; 040import org.ametys.core.right.RightManager; 041import org.ametys.core.right.RightManager.RightResult; 042import org.ametys.core.user.CurrentUserProvider; 043import org.ametys.core.user.UserIdentity; 044import org.ametys.core.user.population.PopulationContextHelper; 045import org.ametys.core.user.population.UserPopulationDAO; 046import org.ametys.plugins.repository.AmetysObject; 047import org.ametys.plugins.repository.AmetysObjectIterable; 048import org.ametys.plugins.repository.AmetysObjectResolver; 049import org.ametys.plugins.repository.query.SortCriteria; 050import org.ametys.plugins.repository.query.expression.Expression; 051import org.ametys.web.filter.SharedContentsHelper; 052import org.ametys.web.site.SiteColorsComponent; 053import org.ametys.web.site.SiteConfigurationManager; 054import org.ametys.web.skin.Skin; 055import org.ametys.web.skin.SkinsManager; 056 057/** 058 * Action for getting the list of existing {@link Site}. 059 */ 060public class GetSitesAction extends ServiceableAction 061{ 062 /** The site manager */ 063 protected SiteManager _siteManager; 064 /** The rights manager */ 065 protected RightManager _rightManager; 066 /** The current user provider */ 067 protected CurrentUserProvider _userProvider; 068 /** The population context helper */ 069 protected PopulationContextHelper _populationContextHelper; 070 /** The group directory context helper */ 071 protected GroupDirectoryContextHelper _groupDirectoryContextHelper; 072 /** The user population DAO */ 073 protected UserPopulationDAO _userPopulationDAO; 074 /** The group directory DAO */ 075 protected GroupDirectoryDAO _groupDirectoryDAO; 076 /** Site type EP */ 077 protected SiteTypesExtensionPoint _siteTypeExtensionPoint; 078 /** Site configuration */ 079 protected SiteConfigurationManager _siteConfigurationManager; 080 /** Ametys resolver */ 081 protected AmetysObjectResolver _ametysResolver; 082 /** Handle site colors */ 083 protected SiteColorsComponent _siteColors; 084 /** The skins manager */ 085 protected SkinsManager _skinsManager; 086 087 @Override 088 public void service(ServiceManager smanager) throws ServiceException 089 { 090 super.service(smanager); 091 _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE); 092 _rightManager = (RightManager) smanager.lookup(RightManager.ROLE); 093 _userProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 094 _siteTypeExtensionPoint = (SiteTypesExtensionPoint) smanager.lookup(SiteTypesExtensionPoint.ROLE); 095 _siteConfigurationManager = (SiteConfigurationManager) smanager.lookup(SiteConfigurationManager.ROLE); 096 _ametysResolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 097 _populationContextHelper = (PopulationContextHelper) smanager.lookup(PopulationContextHelper.ROLE); 098 _groupDirectoryContextHelper = (GroupDirectoryContextHelper) smanager.lookup(GroupDirectoryContextHelper.ROLE); 099 _userPopulationDAO = (UserPopulationDAO) smanager.lookup(UserPopulationDAO.ROLE); 100 _groupDirectoryDAO = (GroupDirectoryDAO) smanager.lookup(GroupDirectoryDAO.ROLE); 101 _siteColors = (SiteColorsComponent) smanager.lookup(SiteColorsComponent.ROLE); 102 _skinsManager = (SkinsManager) smanager.lookup(SkinsManager.ROLE); 103 } 104 105 @Override 106 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 107 { 108 Request request = ObjectModelHelper.getRequest(objectModel); 109 Map<String, Object> result = new HashMap<>(); 110 111 UserIdentity user = _userProvider.getUser(); 112 113 String id = request.getParameter("id"); 114 115 boolean recursive = parameters.getParameterAsBoolean("recursive", BooleanUtils.toBoolean(request.getParameter("recursive"))); 116 boolean readAccessOnly = parameters.getParameterAsBoolean("readAccessOnly", BooleanUtils.toBoolean(request.getParameter("readAccessOnly"))); 117 boolean sharedSitesOnly = parameters.getParameterAsBoolean("sharedSitesOnly", BooleanUtils.toBoolean(request.getParameter("sharedSitesOnly"))); 118 119 List<Map<String, Object>> sites = new ArrayList<>(); 120 121 try (AmetysObjectIterable<Site> rootSites = _getRootSites(id);) 122 { 123 String currentSiteName = (String) request.getAttribute("siteName"); 124 125 for (Site site : rootSites) 126 { 127 sites.addAll(_rootSite2json(request, site, currentSiteName, recursive, readAccessOnly, sharedSitesOnly, user)); 128 } 129 } 130 131 result.put("sites", sites); 132 133 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 134 135 return EMPTY_MAP; 136 } 137 138 /** 139 * Get root sites 140 * @param id Optionnal root id 141 * @return Ametys iterable of sites 142 */ 143 protected AmetysObjectIterable<Site> _getRootSites(String id) 144 { 145 if (StringUtils.isNotEmpty(id)) 146 { 147 AmetysObject ao = _ametysResolver.resolveById(id); 148 149 if (ao instanceof Site) 150 { 151 return ((Site) ao).getChildrenSites(); 152 } 153 } 154 return _siteManager.getRootSites(); 155 } 156 157 /** 158 * Retrieves the JSON for a root site 159 * @param request The request 160 * @param site The root site 161 * @param currentSiteName The name of the current site 162 * @param recursive True to handle child sites 163 * @param readAccessOnly Only read access site 164 * @param sharedSitesOnly Only shared sites 165 * @param user The current user 166 * @return The root site info 167 */ 168 protected List<Map<String, Object>> _rootSite2json(Request request, Site site, String currentSiteName, boolean recursive, boolean readAccessOnly, boolean sharedSitesOnly, UserIdentity user) 169 { 170 List<Map<String, Object>> sites = new ArrayList<>(); 171 172 if (recursive) 173 { 174 // on recursion, returns all sub sites on the same level. The real level is determined by the "depth" property 175 sites.addAll(_childSites2json(request, site, currentSiteName, 0, readAccessOnly, sharedSitesOnly)); 176 } 177 else 178 { 179 sites.add(_site2json(request, site, currentSiteName, 0, readAccessOnly, sharedSitesOnly)); 180 } 181 182 return sites; 183 } 184 185 /** 186 * Child site to JSON 187 * @param request The request 188 * @param site The root site 189 * @param currentSiteName The name of the current site 190 * @param depth The depth from the root site 191 * @param readAccessOnly Only read access site 192 * @param sharedSitesOnly Only shared sites 193 * @return The child sites info 194 */ 195 protected List<Map<String, Object>> _childSites2json (Request request, Site site, String currentSiteName, int depth, boolean readAccessOnly, boolean sharedSitesOnly) 196 { 197 List<Map<String, Object>> childrenAndItseft = new ArrayList<>(); 198 199 childrenAndItseft.add(_site2json(request, site, currentSiteName, depth, readAccessOnly, sharedSitesOnly)); 200 201 // Get child site recursively 202 for (Site child : site.getChildrenSites()) 203 { 204 childrenAndItseft.addAll(_childSites2json(request, child, currentSiteName, depth + 1, readAccessOnly, sharedSitesOnly)); 205 } 206 207 return childrenAndItseft; 208 } 209 210 /** 211 * Site to JSON 212 * @param request The request 213 * @param site The root site 214 * @param currentSiteName The name of the current site 215 * @param depth The depth from the root site 216 * @param readAccessOnly Only read access site 217 * @param sharedSitesOnly Only shared sites 218 * @return The site info 219 */ 220 protected Map<String, Object> _site2json (Request request, Site site, String currentSiteName, int depth, boolean readAccessOnly, boolean sharedSitesOnly) 221 { 222 Map<String, Object> object = new HashMap<>(); 223 224 // Calculate shared contents only if necessary 225 long sharedContentsCount = sharedSitesOnly ? _sharedContentsSize(site, currentSiteName) : 0; 226 227 boolean readAccessCondition = !readAccessOnly 228 || readAccessOnly && canRead(site, request); 229 boolean sharedSiteCondition = !sharedSitesOnly 230 || sharedSitesOnly && sharedContentsCount > 0; 231 232 if (readAccessCondition && sharedSiteCondition) 233 { 234 object = _site2json(site); 235 object.put("depth", depth); 236 object.put("current", site.getName().equals(currentSiteName)); 237 object.put("sharedContentsCount", sharedContentsCount); 238 239 if (site.getChildrenSiteNames().isEmpty()) 240 { 241 object.put("sites", new ArrayList<>()); 242 } 243 } 244 return object; 245 } 246 247 /** 248 * Get the JSON representation of a site 249 * @param site The site 250 * @return The site as JSON object 251 */ 252 protected Map<String, Object> _site2json (Site site) 253 { 254 Map<String, Object> object = new HashMap<>(); 255 256 object.put("id", site.getId()); 257 object.put("name", site.getName()); 258 object.put("title", StringUtils.defaultString(site.getTitle())); 259 object.put("description", StringUtils.defaultString(site.getDescription())); 260 object.put("url", StringUtils.defaultString(site.getUrl())); 261 object.put("valid", _siteConfigurationManager.isSiteConfigurationValid(site)); 262 263 object.put("color", site.getColor()); 264 String colorIndex = site.getColor(); 265 if (!_siteColors.getColors().containsKey(colorIndex)) 266 { 267 colorIndex = _siteColors.getDefaultKey(); 268 } 269 object.put("colorIndex", colorIndex); 270 271 SiteType siteType = _siteTypeExtensionPoint.getExtension(site.getType()); 272 273 String iconGlyph = siteType.getIconGlyph(); 274 if (iconGlyph != null) 275 { 276 object.put("iconGlyph", iconGlyph); 277 } 278 279 object.put("iconSmall", siteType.getSmallIcon()); 280 object.put("iconLarge", siteType.getLargeIcon()); 281 object.put("typeLabel", siteType.getLabel()); 282 object.put("type", siteType.getId()); 283 object.put("privateType", siteType.isPrivateType()); 284 285 String skinId = site.getSkinId(); 286 object.put("skin", skinId); 287 288 Skin skin = _skinsManager.getSkin(skinId); 289 if (skin != null) 290 { 291 object.put("skinLabel", skin.getLabel()); 292 } 293 294 object.put("populations", _populationContextHelper.getUserPopulationsOnContext("/sites/" + site.getName(), true).stream() 295 .map(_userPopulationDAO::getUserPopulation) 296 .filter(Objects::nonNull) 297 .map(_userPopulationDAO::getUserPopulationAsJson) 298 .collect(Collectors.toList())); 299 object.put("populationsFO", _populationContextHelper.getUserPopulationsOnContext("/sites-fo/" + site.getName(), true).stream() 300 .map(_userPopulationDAO::getUserPopulation) 301 .filter(Objects::nonNull) 302 .map(_userPopulationDAO::getUserPopulationAsJson) 303 .collect(Collectors.toList())); 304 305 object.put("groups", _groupDirectoryContextHelper.getGroupDirectoriesOnContext("/sites/" + site.getName()).stream() 306 .map(_groupDirectoryDAO::getGroupDirectory) 307 .filter(Objects::nonNull) 308 .map(_groupDirectoryDAO::getGroupDirectory2Json) 309 .collect(Collectors.toList())); 310 object.put("groupsFO", _groupDirectoryContextHelper.getGroupDirectoriesOnContext("/sites-fo/" + site.getName()).stream() 311 .map(_groupDirectoryDAO::getGroupDirectory) 312 .filter(Objects::nonNull) 313 .map(_groupDirectoryDAO::getGroupDirectory2Json) 314 .collect(Collectors.toList())); 315 316 return object; 317 } 318 319 private long _sharedContentsSize (Site site, String currentSiteName) 320 { 321 if (currentSiteName == null) 322 { 323 return 0; 324 } 325 326 Site currentSite = _siteManager.getSite(currentSiteName); 327 Expression expr = SharedContentsHelper.getSharedContentsExpression(currentSite, site); 328 329 SortCriteria sortCriteria = new SortCriteria(); 330 sortCriteria.addCriterion("title", true, true); 331 String xPathQuery = org.ametys.plugins.repository.query.QueryHelper.getXPathQuery(null, "ametys:content", expr, sortCriteria); 332 333 AmetysObjectIterable<Content> contents = _ametysResolver.query(xPathQuery); 334 return contents.getSize(); 335 } 336 337 /** 338 * Determines if the current user has a read access on site 339 * @param site the site 340 * @param request the request 341 * @return true if current user has read access 342 */ 343 protected boolean canRead (Site site, Request request) 344 { 345 String currentSiteName = (String) request.getAttribute("siteName"); 346 347 try 348 { 349 request.setAttribute("siteName", site.getName()); 350 return _rightManager.hasRight(_userProvider.getUser(), "Web_Right_Site_See_Contents", "/cms") == RightResult.RIGHT_ALLOW; 351 } 352 finally 353 { 354 request.setAttribute("siteName", currentSiteName); 355 } 356 } 357 358}