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.page;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Set;
023
024import org.apache.avalon.framework.component.Component;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.commons.lang3.StringUtils;
028
029import org.ametys.core.right.RightManager;
030import org.ametys.core.right.RightManager.RightResult;
031import org.ametys.core.ui.Callable;
032import org.ametys.core.user.CurrentUserProvider;
033import org.ametys.core.user.UserIdentity;
034import org.ametys.plugins.repository.AmetysObject;
035import org.ametys.plugins.repository.AmetysObjectIterable;
036import org.ametys.plugins.repository.AmetysObjectResolver;
037import org.ametys.plugins.repository.UnknownAmetysObjectException;
038import org.ametys.web.repository.site.Site;
039import org.ametys.web.repository.site.SiteManager;
040import org.ametys.web.repository.sitemap.Sitemap;
041import org.ametys.web.sitemap.SitemapIndicatorsHandler;
042import org.ametys.web.sitemap.SitemapTreeIndicator;
043
044/**
045 * DAO for manipulating pages
046 */
047public class SitemapDAO extends AbstractSitemapElementsDAO implements Component
048{
049    /** The component's role */
050    public static final String ROLE = SitemapDAO.class.getName();
051    
052    private AmetysObjectResolver _resolver;
053    private RightManager _rightManager;
054    private CurrentUserProvider _currentUserProvider;
055    private SiteManager _siteManager;
056    private SitemapIndicatorsHandler _sitemapHandler;
057    
058    @Override
059    public void service(ServiceManager smanager) throws ServiceException
060    {
061        super.service(smanager);
062        
063        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
064        _rightManager = (RightManager) smanager.lookup(RightManager.ROLE);
065        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
066        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
067        _sitemapHandler = (SitemapIndicatorsHandler) smanager.lookup(SitemapIndicatorsHandler.ROLE);
068    }
069    
070    /**
071     * Get sitemap properties
072     * @param sitemapId the sitemap ID
073     * @return the sitemap's properties or null if not found
074     */
075    public Map<String, Object> getSitemap(String sitemapId)
076    {
077        return getSitemap(sitemapId, null, null);
078    }
079    
080    /**
081     * Get sitemap properties
082     * @param sitemapId the sitemap ID
083     * @param zoneName The optional zone name to limit informations of zones to that zone. Can be null or empty to avoid limitation.
084     * @param zoneItemId The optional zone item identifier to limit informations of zones to that zone item. Can be null or empty to avoid limitation.
085     * @return the sitemap's properties or null if not found
086     */
087    @Callable(rights = Callable.NO_CHECK_REQUIRED) // Required by the SitemapMessageTargetFactory
088    public Map<String, Object> getSitemap(String sitemapId, String zoneName, String zoneItemId)
089    {
090        try
091        {
092            Sitemap sitemap = _resolver.resolveById(sitemapId);
093            
094            Map<String, Object> infos = new HashMap<>();
095            
096            infos.put("id", sitemap.getId());
097            infos.put("name", sitemap.getName());
098            infos.put("siteName", sitemap.getSiteName());
099            infos.put("isModifiable", true);
100            infos.put("rights", getUserRights(sitemap));
101            
102            String type = sitemap.getTemplate() != null ? "container" : "node";
103            infos.put("type", type);
104
105            // limitation information
106            if (StringUtils.isNotBlank(zoneName))
107            {
108                infos.put("zoneName", zoneName);
109            }
110            if (StringUtils.isNotBlank(zoneItemId))
111            {
112                infos.put("zoneItemId", zoneItemId);
113            }
114            
115            switch (type)
116            {
117                case "container":
118                    infos.putAll(_sitemapElement2json(sitemap, zoneName, zoneItemId));
119                    break;
120                default:
121            }
122            
123            return infos;
124        }
125        catch (UnknownAmetysObjectException e)
126        {
127            return null;
128        }
129    }
130    
131    /**
132     * Get properties of a site, sitemap or on sites root node
133     * @param siteName the site name. Set to null to get properties of sites root node
134     * @param lang the sitemap language. Set to null to get properties of site node
135     * @return the properties
136     */
137    public Map<String, Object> getProperties(String siteName, String lang)
138    {
139        Map<String, Object> result = new HashMap<>();
140        
141        if (StringUtils.isEmpty(siteName))
142        {
143            // Get information on root sites node
144            AmetysObject root = _siteManager.getRoot();
145            result.put("id", root.getId());
146            result.put("name", root.getId());
147        }
148        else if (StringUtils.isEmpty(lang))
149        {
150            // Get properties on site node
151            Site site = _siteManager.getSite(siteName);
152            result.put("id", site.getId());
153            result.put("name", site.getName());
154            result.put("title", site.getTitle());
155        }
156        else
157        {
158            // Get properties on sitemap node
159            result = getSitemapProperties(siteName, lang, null);
160        }
161                
162        return result;
163    }
164    
165    /**
166     * Get the sitemap properties as JSON object
167     * @param siteName the site name. Set to null to get properties of sites root node
168     * @param lang the sitemap language. Set to null to get properties of site node
169     * @param rights If not null, the sitemap properties will contain a 'isAllowed' property based on user rights. Can be null.
170     * @return the sitemap properties
171     */
172    @Callable(rights = Callable.NO_CHECK_REQUIRED) // required by the select-page widget
173    public Map<String, Object> getSitemapProperties(String siteName, String lang, List<String> rights)
174    {
175        Site site = _siteManager.getSite(siteName);
176        Sitemap sitemap = site.getSitemap(lang);
177        
178        return getSitemapProperties(sitemap, rights);
179    }
180    
181    /**
182     * Get the sitemap properties into a JSON object
183     * @param sitemap The sitemap
184     * @param rightsToCheck If not null, the sitemap properties will contain a 'isAllowed' property based on user rights. Can be null.
185     * @return the sitemap properties
186     */
187    public Map<String, Object> getSitemapProperties(Sitemap sitemap, List<String> rightsToCheck)
188    {
189        Map<String, Object> jsonObject = new HashMap<>();
190        
191        jsonObject.put("id", sitemap.getId());
192        jsonObject.put("name", sitemap.getName());
193        jsonObject.put("lang", sitemap.getName());
194        jsonObject.put("siteName", sitemap.getSiteName());
195        
196        if (rightsToCheck != null)
197        {
198            jsonObject.put("isAllowed", isCurrentUserAllowed(sitemap, rightsToCheck));
199        }
200        
201        return jsonObject;
202    }
203    
204    /**
205     * Get the properties of a page in order to display it into a sitemap tree
206     * @param pageId The page's id
207     * @return The page's properties
208     */
209    @Callable(rights = "Web_Rights_Tool_Sitemap")
210    public Map<String, Object> getPageProperties(String pageId)
211    {
212        return getPageProperties((Page) _resolver.resolveById(pageId));
213    }
214    
215    /**
216     * Get the properties of a page in order to display it into a sitemap tree
217     * @param page The page
218     * @return The page's properties
219     */
220    public Map<String, Object> getPageProperties(Page page)
221    {
222        return getPageProperties(page, null);
223    }
224    
225    /**
226     * Get the properties of a page in order to display it into a sitemap tree
227     * @param page The page
228     * @param rightsToCheck If not null, the page properties will contain a 'isAllowed' property based on user rights
229     * @return The page's properties
230     */
231    public Map<String, Object> getPageProperties(Page page, List<String> rightsToCheck)
232    {
233        boolean hasChild = page.getChildrenPages().iterator().hasNext();
234        boolean isLocked = page instanceof LockablePage lockablePage && lockablePage.isLocked();
235        Map<String, Object> jsonObject = new HashMap<>();
236        
237        jsonObject.put("id", page.getId());
238        jsonObject.put("name", page.getName());
239        jsonObject.put("siteName", page.getSiteName());
240        jsonObject.put("lang", page.getSitemapName());
241        jsonObject.put("title", page.getTitle());
242        jsonObject.put("longTitle", page.getLongTitle());
243        jsonObject.put("type", page.getType());
244        jsonObject.put("template", page.getTemplate());
245        jsonObject.put("path", page.getPathInSitemap());
246        jsonObject.put("moveable", page instanceof MoveablePage && !isLocked);
247        jsonObject.put("modifiable", page instanceof ModifiablePage);
248        jsonObject.put("hasChild", hasChild);
249        jsonObject.put("locked", isLocked);
250        
251        SitemapTreeIndicator icon = _sitemapHandler.getIcon(page);
252        
253        jsonObject.put("iconGlyph", icon.getIconGlyph());
254        jsonObject.put("iconDecorator", icon.getIconDecorator());
255        jsonObject.put("iconSmall", icon.getIcon());
256        
257        List<String> decorators = new ArrayList<>();
258        for (SitemapTreeIndicator decorator : _sitemapHandler.getDecorators(page))
259        {
260            decorators.add(decorator.getId());
261        }
262        jsonObject.put("decorators", StringUtils.join(decorators, ","));
263        
264        if (!hasChild)
265        {
266            jsonObject.put("pages", new ArrayList<>());
267        }
268        
269        if (rightsToCheck != null)
270        {
271            jsonObject.put("isAllowed", isCurrentUserAllowed(page, rightsToCheck));
272        }
273        return jsonObject;
274    }
275    
276    /**
277     * Determines if the current user has the requested right on this sitemap element
278     * @param sitemapElement The sitemap element
279     * @param rights The rights to check
280     * @return true if the current user is allowed
281     */
282    public boolean isCurrentUserAllowed(SitemapElement sitemapElement, List<String> rights)
283    {
284        if (rights == null || rights.isEmpty())
285        {
286            return true;
287        }
288        
289        for (String rightId : rights)
290        {
291            if (_rightManager.currentUserHasRight(rightId, sitemapElement) != RightResult.RIGHT_ALLOW)
292            {
293                return false;
294            }
295        }
296        
297        return true;
298    }
299    
300    /**
301     * Get the path of pages which match filter regexp
302     * @param id The id of page or sitemap to start search
303     * @param value the value to match
304     * @return the matching paths
305     */
306    @Callable(rights = Callable.NO_CHECK_REQUIRED) // required by the select-page widget
307    public List<String> filterPagesByRegExp(String id, String value)
308    {
309        List<String> matchingPaths = new ArrayList<>();
310
311        SitemapElement container = _resolver.resolveById(id);
312        
313        String toMatch = StringUtils.stripAccents(value.toLowerCase()).trim();
314        
315        AmetysObjectIterable< ? extends Page> childrenPages = container.getChildrenPages();
316        for (Page childPage : childrenPages)
317        {
318            _getMatchingPages(childPage, toMatch, matchingPaths);
319        }
320        
321        return matchingPaths;
322    }
323    
324    private void _getMatchingPages(Page page, String value, List<String> matchingPaths)
325    {
326        String title =  StringUtils.stripAccents(page.getTitle().toLowerCase());
327        if (title.contains(value))
328        {
329            matchingPaths.add(page.getPathInSitemap());
330        }
331        
332        AmetysObjectIterable< ? extends Page> childrenPages = page.getChildrenPages();
333        for (Page childPage : childrenPages)
334        {
335            _getMatchingPages (childPage, value, matchingPaths);
336        }
337    }
338    
339    /**
340     * Get the sitemap's decorators
341     * @param siteName the site name
342     * @return the list of decorators
343     */
344    public List<Map<String, Object>> getDecorators (String siteName)
345    {
346        Site site = _siteManager.getSite(siteName);
347        
348        List<Map<String, Object>> decorators = new ArrayList<>();
349        
350        for (SitemapTreeIndicator decorator : _sitemapHandler.getDecorators(site))
351        {
352            Map<String, Object> object = new HashMap<>();
353            object.put("id", decorator.getId());
354            object.put("label", decorator.getLabel());
355            
356            if (StringUtils.isNotBlank(decorator.getIcon()))
357            {
358                object.put("icon", decorator.getIcon());
359            }
360            if (StringUtils.isNotBlank(decorator.getIconGlyph()))
361            {
362                object.put("iconGlyph", decorator.getIconGlyph());
363            }
364            
365            decorators.add(object);
366        }
367        
368        return decorators;
369    }
370    
371    /**
372     * Get the id of a page from its site name, language and sitemap's path
373     * @param siteName The site name
374     * @param lang The language
375     * @param path the page's path. Can be null to get sitemap's id
376     * @return the page id or null if not found
377     */
378    @Callable(rights = Callable.NO_CHECK_REQUIRED)
379    public String convertPathToId (String siteName, String lang, String path)
380    {
381        assert lang != null;
382        assert siteName != null;
383
384        Site site = _siteManager.getSite(siteName);
385        assert site != null;
386        
387        Sitemap sitemap = site.getSitemap(lang);
388        assert sitemap != null;
389
390        try
391        {
392            if (path != null)
393            {
394                return sitemap.getChild(path).getId();
395            }
396            else
397            {
398                return sitemap.getId();
399            }
400        }
401        catch (UnknownAmetysObjectException e)
402        {
403            return null;
404        }
405    }
406    
407    
408    /**
409     * Get the user rights on a sitemap
410     * @param sitemap the sitemap
411     * @return The user's rights
412     */
413    protected Set<String> getUserRights (Sitemap sitemap)
414    {
415        UserIdentity user = _currentUserProvider.getUser();
416        return _rightManager.getUserRights(user, sitemap);
417    }
418}