001/*
002 *  Copyright 2012 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.plugins.blog;
017
018import java.util.HashMap;
019import java.util.Iterator;
020import java.util.LinkedHashSet;
021import java.util.Map;
022import java.util.Set;
023
024import javax.jcr.Node;
025import javax.jcr.RepositoryException;
026import javax.jcr.Value;
027
028import org.apache.avalon.framework.activity.Initializable;
029import org.apache.avalon.framework.component.Component;
030import org.apache.avalon.framework.logger.AbstractLogEnabled;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.avalon.framework.service.Serviceable;
034
035import org.ametys.plugins.blog.repository.BlogRootPageFactory;
036import org.ametys.plugins.blog.repository.VirtualPostsPage;
037import org.ametys.plugins.blog.repository.VirtualTagsPage;
038import org.ametys.plugins.repository.AmetysObjectIterable;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.repository.AmetysRepositoryException;
041import org.ametys.plugins.repository.UnknownAmetysObjectException;
042import org.ametys.plugins.repository.jcr.JCRAmetysObject;
043import org.ametys.plugins.repository.query.expression.Expression;
044import org.ametys.plugins.repository.query.expression.VirtualFactoryExpression;
045import org.ametys.web.repository.content.WebContent;
046import org.ametys.web.repository.page.Page;
047import org.ametys.web.repository.page.PageQueryHelper;
048import org.ametys.web.repository.page.PagesContainer;
049import org.ametys.web.repository.site.Site;
050import org.ametys.web.repository.site.SiteManager;
051import org.ametys.web.repository.sitemap.Sitemap;
052
053/**
054 * Retrieves blog pages.
055 */
056public class BlogPageHandler extends AbstractLogEnabled implements Component, Initializable, Serviceable
057{
058    
059    /** The avalon role. */
060    public static final String ROLE = BlogPageHandler.class.getName();
061    
062    /** The ametys object resolver. */
063    protected AmetysObjectResolver _resolver;
064    
065    /** The site manager. */
066    protected SiteManager _siteManager;
067    
068    /** The blog root page IDs, indexed by site and sitemap name. */
069    protected Map<String, Map<String, String>> _blogRootPages;
070    
071    /** For each site, indicate if a blog root is present. */
072    protected Map<String, Boolean> _hasBlogRoot;
073    
074    @Override
075    public void service(ServiceManager serviceManager) throws ServiceException
076    {
077        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
078        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
079    }
080    
081    @Override
082    public void initialize() throws Exception
083    {
084        _blogRootPages = new HashMap<>();
085        _hasBlogRoot = new HashMap<>();
086    }
087    
088    /**
089     * Get the blog root page.
090     * @param siteName the current site.
091     * @param sitemapName the current sitemap/language.
092     * @return the blog root page or null if not found.
093     * @throws AmetysRepositoryException if an error occurs.
094     */
095    public PagesContainer getBlogRootPage(String siteName, String sitemapName) throws AmetysRepositoryException
096    {
097        PagesContainer rootPage = null;
098        
099        Map<String, String> siteBlogRootPages = null;
100        
101        if (_blogRootPages.containsKey(siteName))
102        {
103            siteBlogRootPages = _blogRootPages.get(siteName);
104        }
105        else
106        {
107            siteBlogRootPages = new HashMap<>();
108            _blogRootPages.put(siteName, siteBlogRootPages);
109        }
110        
111        if (siteBlogRootPages.containsKey(sitemapName))
112        {
113            String rootId = siteBlogRootPages.get(sitemapName);
114            if (rootId != null)
115            {
116                rootPage = _resolver.resolveById(rootId);
117            }
118        }
119        else
120        {
121            rootPage = _getBlogRootPage(siteName, sitemapName);
122            siteBlogRootPages.put(sitemapName, rootPage == null ? null : rootPage.getId());
123        }
124        
125        return rootPage;
126    }
127    
128    /**
129     * Test if the given site has at least one sitemap with a blog root page.
130     * @param site the site to test.
131     * @return true if the site has at least one sitemap with a blog root page, false otherwise.
132     */
133    public boolean hasBlogRootPage(Site site)
134    {
135        boolean hasOneRoot = false;
136        
137        String siteName = site.getName();
138        
139        if (_hasBlogRoot.containsKey(siteName))
140        {
141            hasOneRoot = _hasBlogRoot.get(siteName);
142        }
143        else
144        {
145            Iterator<Sitemap> sitemaps = site.getSitemaps().iterator();
146            
147            while (sitemaps.hasNext() && !hasOneRoot)
148            {
149                String sitemapName = sitemaps.next().getName();
150                
151                if (getBlogRootPage(site.getName(), sitemapName) != null)
152                {
153                    hasOneRoot = true;
154                }
155            }
156            
157            _hasBlogRoot.put(siteName, hasOneRoot);
158        }
159        
160        return hasOneRoot;
161    }
162    
163    /**
164     * Test if the given page is the blog root page.
165     * @param ametysObject the container ametysObject.
166     * @return true/false.
167     */
168    public boolean isBlogRootPage(JCRAmetysObject ametysObject)
169    {
170        try
171        {
172            Node node = ametysObject.getNode();
173            
174            if (node.hasProperty(AmetysObjectResolver.VIRTUAL_PROPERTY))
175            {
176                Value[] values = node.getProperty(AmetysObjectResolver.VIRTUAL_PROPERTY).getValues();
177                
178                boolean hasValue = false;
179                for (int i = 0; i < values.length && !hasValue; i++)
180                {
181                    hasValue = BlogRootPageFactory.class.getName().equals(values[i].getString());
182                }
183                
184                return hasValue;
185            }
186            else
187            {
188                return false;
189            }
190        }
191        catch (RepositoryException e)
192        {
193            return false;
194        }
195    }
196    
197    /**
198     * Get a page under the blog root.
199     * @param site the current site.
200     * @param language the current language/sitemap.
201     * @param pagePath the wanted page path.
202     * @return the blog page or null if not found.
203     * @throws AmetysRepositoryException if an error occurs
204     */
205    public Page getBlogPage(String site, String language, String pagePath) throws AmetysRepositoryException
206    {
207        Page page = null;
208        
209        PagesContainer rootPage = getBlogRootPage(site, language);
210        if (rootPage != null)
211        {
212            if (rootPage.hasChild(pagePath))
213            {
214                page = rootPage.getChild(pagePath);
215            }
216        }
217        
218        return page;
219    }
220    
221    /**
222     * Get the Page holding the given post content.
223     * @param postContent the post content.
224     * @return the Page holding the post content.
225     */
226    public Page getPostPage(WebContent postContent)
227    {
228        String siteName = postContent.getSiteName();
229        String language = postContent.getLanguage();
230        String postName = postContent.getName();
231        
232        PagesContainer rootPage = getBlogRootPage(siteName, language);
233        Page postsPage = rootPage.getChild(VirtualPostsPage.NAME);
234        
235        Page postPage = null;
236        if (postsPage.hasChild(postName))
237        {
238            postPage = postsPage.getChild(postName);
239        }
240        
241        return postPage;
242    }
243    
244    /**
245     * Get the page holding the given tag
246     * @param siteName The site's name
247     * @param lang The language
248     * @param tagPath The tag's path
249     * @return The page holding this tag or null if not exists
250     */
251    public Page getTagPage (String siteName, String lang, String tagPath)
252    {
253        try
254        {
255            PagesContainer rootPage = getBlogRootPage(siteName, lang);
256            Page tagsPage = rootPage.getChild(VirtualTagsPage.NAME);
257            
258            return tagsPage.getChild(tagPath);
259        }
260        catch (UnknownAmetysObjectException e)
261        {
262            return null;
263        }
264    }
265    
266    /**
267     * Get the pages corresponding to the post Content.
268     * @param postContent the post Content.
269     * @return the post pages.
270     */
271    @Deprecated
272    public Set<Page> getPostPages(WebContent postContent)
273    {
274        Set<Page> postPages = new LinkedHashSet<>();
275        
276        Page postPage = getPostPage(postContent);
277        if (postPage != null)
278        {
279            postPages.add(postPage);
280        }
281        
282        return postPages;
283    }
284    
285    /**
286     * Clear the blog root page cache.
287     */
288    public void clearCache()
289    {
290        _blogRootPages = new HashMap<>();
291        _hasBlogRoot = new HashMap<>();
292    }
293    
294    /**
295     * Clear the blog root page cache for a given site and language.
296     * @param siteName the current site.
297     * @param sitemapName the current sitemap/language.
298     */
299    public void clearCache(String siteName, String sitemapName)
300    {
301        if (_blogRootPages.containsKey(siteName))
302        {
303            _blogRootPages.get(siteName).remove(sitemapName);
304        }
305        
306        _hasBlogRoot.remove(siteName);
307    }
308    
309    /**
310     * Get the blog root page.
311     * @param siteName the current site.
312     * @param sitemapName the current sitemap/language.
313     * @return the blog root page or null if not found.
314     * @throws AmetysRepositoryException if an error occurs.
315     */
316    protected PagesContainer _getBlogRootPage(String siteName, String sitemapName) throws AmetysRepositoryException
317    {
318        PagesContainer page = null;
319        
320        // First, test if the sitemap is the blog root.
321        Sitemap sitemap = _siteManager.getSite(siteName).getSitemap(sitemapName);
322        if (isBlogRootPage(sitemap))
323        {
324            page = sitemap;
325        }
326        else
327        {
328            Expression expression = new VirtualFactoryExpression(BlogRootPageFactory.class.getName());
329            
330            String query = PageQueryHelper.getPageXPathQuery(siteName, sitemapName, null, expression, null);
331            
332            AmetysObjectIterable<PagesContainer> pages = _resolver.query(query);
333            Iterator<PagesContainer> it = pages.iterator();
334            if (it.hasNext())
335            {
336                page = it.next();
337            }
338        }
339        
340        return page;
341    }
342}