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