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.List;
021import java.util.Map;
022import java.util.Map.Entry;
023import java.util.Set;
024import java.util.SortedSet;
025import java.util.stream.Collectors;
026
027import org.apache.commons.lang.StringUtils;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import org.ametys.cms.repository.Content;
032import org.ametys.plugins.repository.AmetysObject;
033import org.ametys.plugins.repository.AmetysObjectIterable;
034import org.ametys.plugins.repository.AmetysRepositoryException;
035import org.ametys.plugins.repository.CollectionIterable;
036import org.ametys.plugins.repository.UnknownAmetysObjectException;
037import org.ametys.plugins.repository.data.holder.ModelLessDataHolder;
038import org.ametys.plugins.repository.data.holder.impl.DefaultModelLessDataHolder;
039import org.ametys.plugins.repository.data.repositorydata.RepositoryData;
040import org.ametys.plugins.repository.data.repositorydata.impl.MemoryRepositoryData;
041import org.ametys.web.repository.page.Page;
042import org.ametys.web.repository.page.virtual.AbstractConfigurableVirtualPage;
043import org.ametys.web.repository.page.virtual.VirtualPageConfiguration;
044
045
046/**
047 * Page representing a second-level page.
048 */
049public class TransitionalPage extends AbstractConfigurableVirtualPage<TransitionalPageFactory>
050{
051    private static Logger _logger = LoggerFactory.getLogger(TransitionalPage.class);
052    private String _path;
053    private int _initialDepth;
054    private String _prefix;
055    
056    /**
057     * Constructor.
058     * @param root the user directory root page.
059     * @param prefix the page's title.
060     * @param path the path
061     * @param configuration The abstract virtual page configuration
062     * @param scheme The scheme
063     * @param factory The transitional page factory
064     */
065    public TransitionalPage(Page root, VirtualPageConfiguration configuration, String scheme, TransitionalPageFactory factory, String prefix, String path)
066    {
067        super(root, configuration, scheme, factory);
068        
069        _prefix = prefix;
070        _path = path;
071        
072        _initialDepth = _factory.getUserDirectoryPageHandler().getDepth(_root);
073    }
074    
075    @Override
076    public int getDepth() throws AmetysRepositoryException
077    {
078        return _root.getDepth() + _path.split("/").length;
079    }
080
081    @Override
082    public Set<String> getReferers() throws AmetysRepositoryException
083    {
084        return null;
085    }
086
087    @Override
088    public String getTitle() throws AmetysRepositoryException
089    {
090        return StringUtils.upperCase(_prefix);
091    }
092    
093    @Override
094    public String getLongTitle() throws AmetysRepositoryException
095    {
096        return StringUtils.upperCase(_prefix);
097    }
098
099    @Override
100    public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException
101    {
102        List<Page> children = new ArrayList<>();
103
104        int depth = _initialDepth - _path.split("/").length;
105        if (depth > 0)
106        {
107            SortedSet<String> transitionalPagesName = _factory.getUserDirectoryPageHandler().getTransitionalPagesName(_root, _factory.getUserDirectoryPageHandler().getName(_path));
108            for (String name : transitionalPagesName)
109            {
110                String pathName = _factory.getUserDirectoryPageHandler().getPathName(name);
111                children.add(_factory.createTransitionalPage(_root, name, _path + "/" + pathName));
112            }
113            
114            Map<String, String> userPagesContent = _factory.getUserDirectoryPageHandler().getUserPagesContent(_root, _factory.getUserDirectoryPageHandler().getName(_path));
115            for (Entry<String, String> entry : userPagesContent.entrySet())
116            {
117                String contentTypeId = _factory.getUserDirectoryPageHandler().getContentTypeId(_root);
118                String classificationAttribute = _factory.getUserDirectoryPageHandler().getClassificationAttribute(_root);
119                Content content;
120                try
121                {
122                    content = _factory.getResolver().resolveById(entry.getValue());
123                }
124                catch (AmetysRepositoryException e)
125                {
126                    // content does not exist, skip to next iteration
127                    break;
128                }
129                if (content == null || !Arrays.asList(content.getTypes()).contains(contentTypeId) || !content.hasValue(classificationAttribute))
130                {
131                    break;
132                }
133                
134                String classificationAttributeValue = _factory.getUserDirectoryPageHandler().getTransformedClassificationValue(_root, content);
135                if (classificationAttributeValue != null && classificationAttributeValue.length() == _path.split("/").length)
136                {
137                    children.add(_factory.getUserPageFactory().createUserPage(_root, content, _path));
138                }
139            }
140        }
141        else
142        {
143            Map<String, String> userPagesContent = _factory.getUserDirectoryPageHandler().getUserPagesContent(_root, _path);
144            for (String contentId : userPagesContent.values())
145            {
146                try
147                {
148                    Content content = _factory.getResolver().resolveById(contentId);
149                    children.add(_factory.getUserPageFactory().createUserPage(_root, content, _path));
150                }
151                catch (UnknownAmetysObjectException e)
152                {
153                    _logger.warn("Content {} does not exist anymore", contentId, e);
154                }
155            }
156        }
157        
158        return new CollectionIterable<>(children);
159    }
160
161    @Override
162    public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePage) throws AmetysRepositoryException
163    {
164        if (includeInvisiblePage)
165        {
166            return getChildrenPages();
167        }
168        else
169        {
170            List<Page> children = new ArrayList<>();
171            return new CollectionIterable<>(children);
172        }
173    }
174    
175    @Override
176    public String getPathInSitemap() throws AmetysRepositoryException
177    {
178        String path = StringUtils.lowerCase(_path);
179        return _root.getPathInSitemap() + "/" + path;
180    }
181
182    @SuppressWarnings("unchecked")
183    @Override
184    public <A extends AmetysObject> A getChild(String path) throws AmetysRepositoryException, UnknownAmetysObjectException
185    {
186        if (path.isEmpty())
187        {
188            throw new AmetysRepositoryException("path must be non empty");
189        }
190        
191        String completePath = _path + "/" + path;
192        int depth = _initialDepth - completePath.split("/").length + 1;
193        if (depth > 0)
194        {
195            String namePath = StringUtils.substringAfterLast(completePath, "/");
196            String parentPath = StringUtils.substringBeforeLast(completePath, "/");
197
198            SortedSet<String> transitionalPagesName = _factory.getUserDirectoryPageHandler().getTransitionalPagesName(_root, parentPath);
199            Map<String, String> userPagesContent = _factory.getUserDirectoryPageHandler().getUserPagesContent(_root, parentPath);
200            String name = _factory.getUserDirectoryPageHandler().getName(namePath);
201            if (transitionalPagesName.contains(name))
202            {
203                TransitionalPage page = _factory.createTransitionalPage(_root, name, completePath);
204                return (A) page;
205            }
206            else if (userPagesContent.containsKey(name))
207            {
208                String contentId = userPagesContent.get(name);
209                Content syncContent = _factory.getResolver().resolveById(contentId);
210                UserPage page = _factory.getUserPageFactory().createUserPage(_root, syncContent, parentPath);
211                return (A) page;
212            }
213            else
214            {
215                throw new UnknownAmetysObjectException("No transitional page named " + name + " (full page path " + path + ").");
216            }
217        }
218        else
219        {
220            String userPath = StringUtils.substringBeforeLast(completePath, "/");
221            String contentName = StringUtils.substringAfterLast(completePath, "/");
222            
223            Map<String, String> userPagesContent = _factory.getUserDirectoryPageHandler().getUserPagesContent(_root, userPath);
224            if (userPagesContent.containsKey(contentName))
225            {
226                Content content = _factory.getResolver().resolveById(userPagesContent.get(contentName));
227                
228                UserPage page = _factory.getUserPageFactory().createUserPage(_root, content, userPath);
229                return (A) page;
230            }
231            else
232            {
233                throw new UnknownAmetysObjectException("No user content named " + contentName + " (full page path " + path + ").");
234            }
235        }
236    }
237
238    @SuppressWarnings("unchecked")
239    @Override
240    public AmetysObjectIterable<? extends AmetysObject> getChildren() throws AmetysRepositoryException
241    {
242        return getChildrenPages();
243    }
244
245    @Override
246    public boolean hasChild(String name) throws AmetysRepositoryException
247    {
248        int depth = _initialDepth - _path.split("/").length;
249        if (depth > 0)
250        {
251            SortedSet<String> transitionalPagesName = _factory.getUserDirectoryPageHandler().getTransitionalPagesName(_root, _path);
252            Map<String, String> userPagesContent = _factory.getUserDirectoryPageHandler().getUserPagesContent(_root, _path);
253            return transitionalPagesName.contains(name) || userPagesContent.containsKey(name);
254        }
255        else
256        {
257            Map<String, String> userPagesContent = _factory.getUserDirectoryPageHandler().getUserPagesContent(_root, _path);
258            return userPagesContent.containsKey(name);
259        }
260    }
261    
262    @Override
263    public String getId() throws AmetysRepositoryException
264    {
265        // E.g: udtransitional://path?rootId=...
266        return "udtransitional://" + _path + "?rootId=" + _root.getId();
267    }
268
269    @Override
270    public String getName() throws AmetysRepositoryException
271    {
272        return StringUtils.lowerCase(_prefix);
273    }
274    
275    @SuppressWarnings("unchecked")
276    @Override
277    public Page getParent() throws AmetysRepositoryException
278    {
279        if (_path.split("/").length > 1)
280        {
281            String parentPath = StringUtils.substringBeforeLast(_path, "/");
282            String pathName = parentPath;
283            if (StringUtils.contains(pathName, "/"))
284            {
285                pathName = StringUtils.substringAfterLast(pathName, "/");
286            }
287            
288            String name = _factory.getUserDirectoryPageHandler().getName(pathName);
289            return _factory.createTransitionalPage(_root, name, parentPath);
290        }
291        else
292        {
293            return _root;
294        }
295    }
296
297    @Override
298    public String getParentPath() throws AmetysRepositoryException
299    {
300        if (_path.split("/").length > 1)
301        {
302            String path = StringUtils.lowerCase(_path);
303            return _root.getPath() + "/" + StringUtils.substringBeforeLast(path, "/");
304        }
305        else
306        {
307            return _root.getPath();
308        }
309    }
310
311    public ModelLessDataHolder getDataHolder()
312    {
313        RepositoryData repositoryData = new MemoryRepositoryData(getName());
314        return new DefaultModelLessDataHolder(_factory.getPageDataTypeEP(), repositoryData);
315    }
316
317    @Override
318    public boolean isVisible() throws AmetysRepositoryException
319    {
320        return false;
321    }
322
323    @Override
324    public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException
325    {
326        return getChildrenPages().stream().collect(Collectors.toList()).get(index);
327    }
328}