001/*
002 *  Copyright 2016 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.userdirectory.page;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.Collections;
021import java.util.List;
022import java.util.Map;
023import java.util.Map.Entry;
024import java.util.stream.Collectors;
025import java.util.Set;
026import java.util.SortedSet;
027
028import org.apache.commons.lang.StringUtils;
029
030import org.ametys.cms.repository.Content;
031import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollectionDAO;
032import org.ametys.plugins.explorer.resources.ResourceCollection;
033import org.ametys.plugins.repository.AmetysObject;
034import org.ametys.plugins.repository.AmetysObjectIterable;
035import org.ametys.plugins.repository.AmetysObjectResolver;
036import org.ametys.plugins.repository.AmetysRepositoryException;
037import org.ametys.plugins.repository.CollectionIterable;
038import org.ametys.plugins.repository.UnknownAmetysObjectException;
039import org.ametys.plugins.repository.metadata.CompositeMetadata;
040import org.ametys.plugins.userdirectory.UserDirectoryPageHandler;
041import org.ametys.web.repository.page.Page;
042import org.ametys.web.repository.page.UnknownZoneException;
043import org.ametys.web.repository.page.Zone;
044import org.ametys.web.repository.site.Site;
045import org.ametys.web.repository.sitemap.Sitemap;
046import org.ametys.web.skin.SkinsManager;
047
048
049/**
050 * Page representing a second-level page.
051 */
052public class TransitionalPage implements Page
053{
054    private Page _root;
055    private String _path;
056    private int _initialDepth;
057    private String _prefix;
058    private AmetysObjectResolver _resolver;
059    private UserDirectoryPageHandler _userDirectoryPageHandler;
060    private SynchronizableContentsCollectionDAO _syncContentsCollectionDAO;
061    private SkinsManager _skinsManager;
062    
063    /**
064     * Constructor.
065     * @param root the user directory root page.
066     * @param prefix the page's title.
067     * @param resolver the {@link AmetysObjectResolver}.
068     * @param path the path
069     * @param userDirectoryPageHandler the user directory page handler component
070     * @param syncContentsCollectionDAO The DAO for synchronizable collections
071     * @param skinsManager the skins manager
072     */
073    public TransitionalPage(Page root, String prefix, String path, AmetysObjectResolver resolver, UserDirectoryPageHandler userDirectoryPageHandler, SynchronizableContentsCollectionDAO syncContentsCollectionDAO, SkinsManager skinsManager)
074    {
075        _root = root;
076        _prefix = prefix;
077        _path = path;
078        _resolver = resolver;
079        _userDirectoryPageHandler = userDirectoryPageHandler;
080        _syncContentsCollectionDAO = syncContentsCollectionDAO;
081        _skinsManager = skinsManager;
082        
083        _initialDepth = _userDirectoryPageHandler.getDepth(_root);
084    }
085    
086    @Override
087    public int getDepth() throws AmetysRepositoryException
088    {
089        return _root.getDepth() + _path.split("/").length;
090    }
091
092    @Override
093    public Set<String> getReferers() throws AmetysRepositoryException
094    {
095        return null;
096    }
097
098    @Override
099    public ResourceCollection getRootAttachments() throws AmetysRepositoryException
100    {
101        return null;
102    }
103
104    @Override
105    public String getTemplate() throws AmetysRepositoryException
106    {
107        return "page";
108    }
109
110    @Override
111    public String getTitle() throws AmetysRepositoryException
112    {
113        return StringUtils.upperCase(_prefix);
114    }
115    
116    @Override
117    public String getLongTitle() throws AmetysRepositoryException
118    {
119        return StringUtils.upperCase(_prefix);
120    }
121
122    @Override
123    public PageType getType() throws AmetysRepositoryException
124    {
125        return PageType.CONTAINER;
126    }
127
128    @Override
129    public String getURL() throws AmetysRepositoryException
130    {
131        throw new UnsupportedOperationException("getURL not supported on virtual user directory pages");
132    }
133
134    @Override
135    public LinkType getURLType() throws AmetysRepositoryException
136    {
137        throw new UnsupportedOperationException("getURLType not supported on virtual user directory pages");
138    }
139
140    @Override
141    public Zone getZone(String name) throws UnknownZoneException, AmetysRepositoryException
142    {
143        if (!"default".equals(name))
144        {
145            throw new IllegalArgumentException("Only the zone named 'default' is actually supported on virtual transitional pages.");
146        }
147        
148        return new TransitionalZone(this);
149    }
150
151    @Override
152    public AmetysObjectIterable< ? extends Zone> getZones() throws AmetysRepositoryException
153    {
154        List<Zone> zones = new ArrayList<>();
155        zones.add(new TransitionalZone(this));
156        return new CollectionIterable<>(zones);
157    }
158
159    @Override
160    public boolean hasZone(String name) throws AmetysRepositoryException
161    {
162        return "default".equals(name);
163    }
164
165    @Override
166    public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException
167    {
168        ArrayList<Page> children = new ArrayList<>();
169
170        int depth = _initialDepth - _path.split("/").length;
171        if (depth > 0)
172        {
173            SortedSet<String> transitionalPagesName = _userDirectoryPageHandler.getTransitionalPagesName(_root, _userDirectoryPageHandler.getName(_path));
174            for (String name : transitionalPagesName)
175            {
176                String pathName = _userDirectoryPageHandler.getPathName(name);
177                children.add(new TransitionalPage(_root, name, _path + "/" + pathName, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager));
178            }
179            
180            Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, _userDirectoryPageHandler.getName(_path));
181            for (Entry<String, String> entry : userPagesContent.entrySet())
182            {
183                String contentTypeId = _userDirectoryPageHandler.getContentTypeId(_root);
184                String classificationMetadata = _userDirectoryPageHandler.getClassificationMetadata(_root);
185                Content content;
186                try
187                {
188                    content = _resolver.resolveById(entry.getValue());
189                }
190                catch (AmetysRepositoryException e)
191                {
192                    // content does not exist, skip to next iteration
193                    break;
194                }
195                if (content == null || !Arrays.asList(content.getTypes()).contains(contentTypeId) || !content.getMetadataHolder().hasMetadata(classificationMetadata))
196                {
197                    break;
198                }
199                
200                String metadataValue = _userDirectoryPageHandler.getTransformedClassificationMetadataValue(_root, content);
201                if (metadataValue != null && metadataValue.length() == _path.split("/").length)
202                {
203                    children.add(new UserPage(_root, content, _path, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager));
204                }
205            }
206        }
207        else
208        {
209            Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, _path);
210            for (String contentId : userPagesContent.values())
211            {
212                try
213                {
214                    Content content = _resolver.resolveById(contentId);
215                    children.add(new UserPage(_root, content, _path, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager));
216                }
217                catch (UnknownAmetysObjectException e)
218                {
219                    System.out.println("Content does not exist anymore");
220                }
221            }
222        }
223        
224        return new CollectionIterable<>(children);
225    }
226
227    @Override
228    public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePage) throws AmetysRepositoryException
229    {
230        if (includeInvisiblePage)
231        {
232            return getChildrenPages();
233        }
234        else
235        {
236            ArrayList<Page> children = new ArrayList<>();
237            return new CollectionIterable<>(children);
238        }
239    }
240    
241    @Override
242    public String getPathInSitemap() throws AmetysRepositoryException
243    {
244        String path = StringUtils.lowerCase(_path);
245        return _root.getPathInSitemap() + "/" + path;
246    }
247
248    @Override
249    public Site getSite() throws AmetysRepositoryException
250    {
251        return _root.getSite();
252    }
253
254    @Override
255    public String getSiteName() throws AmetysRepositoryException
256    {
257        return _root.getSiteName();
258    }
259
260    @Override
261    public Sitemap getSitemap() throws AmetysRepositoryException
262    {
263        return _root.getSitemap();
264    }
265
266    @Override
267    public String getSitemapName() throws AmetysRepositoryException
268    {
269        return _root.getSitemapName();
270    }
271
272    @SuppressWarnings("unchecked")
273    @Override
274    public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException
275    {
276        if (path.isEmpty())
277        {
278            throw new AmetysRepositoryException("path must be non empty");
279        }
280        
281        String completePath = _path + "/" + path;
282        int depth = _initialDepth - completePath.split("/").length + 1;
283        if (depth > 0)
284        {
285            String namePath = StringUtils.substringAfterLast(completePath, "/");
286            String parentPath = StringUtils.substringBeforeLast(completePath, "/");
287
288            SortedSet<String> transitionalPagesName = _userDirectoryPageHandler.getTransitionalPagesName(_root, parentPath);
289            Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, parentPath);
290            String name = _userDirectoryPageHandler.getName(namePath);
291            if (transitionalPagesName.contains(name))
292            {
293                TransitionalPage page = new TransitionalPage(_root, name, completePath, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager);
294                return (A) page;
295            }
296            else if (userPagesContent.containsKey(name))
297            {
298                String contentId = userPagesContent.get(name);
299                Content syncContent = _resolver.resolveById(contentId);
300                UserPage page = new UserPage(_root, syncContent, parentPath, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager);
301                return (A) page;
302            }
303            else
304            {
305                throw new UnknownAmetysObjectException("No transitional page named " + name + " (full page path " + path + ").");
306            }
307        }
308        else
309        {
310            String userPath = StringUtils.substringBeforeLast(completePath, "/");
311            String contentName = StringUtils.substringAfterLast(completePath, "/");
312            
313            Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, userPath);
314            if (userPagesContent.containsKey(contentName))
315            {
316                Content content = _resolver.resolveById(userPagesContent.get(contentName));
317                UserPage page = new UserPage(_root, content, userPath, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager);
318                return (A) page;
319            }
320            else
321            {
322                throw new UnknownAmetysObjectException("No user content named " + contentName + " (full page path " + path + ").");
323            }
324        }
325    }
326
327    @SuppressWarnings("unchecked")
328    @Override
329    public AmetysObjectIterable<? extends AmetysObject> getChildren() throws AmetysRepositoryException
330    {
331        return getChildrenPages();
332    }
333
334    @Override
335    public boolean hasChild(String name) throws AmetysRepositoryException
336    {
337        int depth = _initialDepth - _path.split("/").length;
338        if (depth > 0)
339        {
340            SortedSet<String> transitionalPagesName = _userDirectoryPageHandler.getTransitionalPagesName(_root, _path);
341            Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, _path);
342            return transitionalPagesName.contains(name) || userPagesContent.containsKey(name);
343        }
344        else
345        {
346            Map<String, String> userPagesContent = _userDirectoryPageHandler.getUserPagesContent(_root, _path);
347            return userPagesContent.containsKey(name);
348        }
349    }
350    
351    @Override
352    public String getId() throws AmetysRepositoryException
353    {
354        // E.g: udtransitional://path?rootId=...
355        return "udtransitional://" + _path + "?rootId=" + _root.getId();
356    }
357
358    @Override
359    public String getName() throws AmetysRepositoryException
360    {
361        return StringUtils.lowerCase(_prefix);
362    }
363    
364    @SuppressWarnings("unchecked")
365    @Override
366    public Page getParent() throws AmetysRepositoryException
367    {
368        if (_path.split("/").length > 1)
369        {
370            String parentPath = StringUtils.substringBeforeLast(_path, "/");
371            String pathName = parentPath;
372            if (StringUtils.contains(pathName, "/"))
373            {
374                pathName = StringUtils.substringAfterLast(pathName, "/");
375            }
376            
377            String name = _userDirectoryPageHandler.getName(pathName);
378            return new TransitionalPage(_root, name, parentPath, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager);
379        }
380        else
381        {
382            return _root;
383        }
384    }
385
386    @Override
387    public String getParentPath() throws AmetysRepositoryException
388    {
389        if (_path.split("/").length > 1)
390        {
391            String path = StringUtils.lowerCase(_path);
392            return _root.getPath() + "/" + StringUtils.substringBeforeLast(path, "/");
393        }
394        else
395        {
396            return _root.getPath();
397        }
398    }
399
400    @Override
401    public String getPath() throws AmetysRepositoryException
402    {
403        return getParentPath() + "/" + getName();
404    }
405
406    @Override
407    public CompositeMetadata getMetadataHolder()
408    {
409        return new StaticCompositeMetadata();
410    }
411
412    @Override
413    public Set<String> getTags() throws AmetysRepositoryException
414    {
415        return Collections.emptySet();
416    }
417
418    @Override
419    public boolean isVisible() throws AmetysRepositoryException
420    {
421        return false;
422    }
423
424    @Override
425    public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException
426    {
427        return getChildrenPages().stream().collect(Collectors.toList()).get(index);
428    }
429}