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