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