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.Collections;
020import java.util.List;
021import java.util.Locale;
022import java.util.Map;
023import java.util.Set;
024
025import javax.jcr.Node;
026import javax.jcr.RepositoryException;
027import javax.jcr.Value;
028
029import org.apache.commons.lang.StringUtils;
030
031import org.ametys.cms.repository.Content;
032import org.ametys.cms.repository.DefaultContent;
033import org.ametys.core.group.GroupIdentity;
034import org.ametys.core.right.ProfileAssignmentStorage.AnonymousOrAnyConnectedKeys;
035import org.ametys.core.right.ProfileAssignmentStorage.UserOrGroup;
036import org.ametys.core.right.RightManager;
037import org.ametys.core.user.UserIdentity;
038import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollection;
039import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollectionDAO;
040import org.ametys.plugins.explorer.resources.ResourceCollection;
041import org.ametys.plugins.repository.ACLAmetysObject;
042import org.ametys.plugins.repository.AmetysObject;
043import org.ametys.plugins.repository.AmetysObjectIterable;
044import org.ametys.plugins.repository.AmetysObjectResolver;
045import org.ametys.plugins.repository.AmetysRepositoryException;
046import org.ametys.plugins.repository.CollectionIterable;
047import org.ametys.plugins.repository.UnknownAmetysObjectException;
048import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder;
049import org.ametys.plugins.repository.data.holder.ModelLessDataHolder;
050import org.ametys.plugins.repository.data.holder.impl.DefaultModelLessDataHolder;
051import org.ametys.plugins.repository.data.repositorydata.RepositoryData;
052import org.ametys.plugins.repository.data.repositorydata.impl.MemoryRepositoryData;
053import org.ametys.plugins.repository.data.type.RepositoryModelItemType;
054import org.ametys.plugins.userdirectory.UserDirectoryPageHandler;
055import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint;
056import org.ametys.web.repository.page.Page;
057import org.ametys.web.repository.page.UnknownZoneException;
058import org.ametys.web.repository.page.Zone;
059import org.ametys.web.repository.site.Site;
060import org.ametys.web.repository.sitemap.Sitemap;
061import org.ametys.web.service.ServiceExtensionPoint;
062import org.ametys.web.skin.Skin;
063import org.ametys.web.skin.SkinsManager;
064
065/**
066 * Page representing a second-level page.
067 */
068public class UserPage implements Page, ACLAmetysObject
069{
070    private static final String __USER_PAGE_TEMPLATE = "user-page";
071    
072    private Page _root;
073    private int _initialDepth;
074    private String _title;
075    private AmetysObjectResolver _resolver;
076    private UserDirectoryPageHandler _userDirectoryPageHandler;
077    private SynchronizableContentsCollectionDAO _syncContentsCollectionDAO;
078    private SkinsManager _skinsManager;
079    private String _path;
080    private Content _syncContent;
081
082    private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _pageDataTypeExtensionPoint;
083    private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _zoneDataTypeExtensionPoint;
084    private ServiceExtensionPoint _serviceExtensionPoint;
085    private AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> _zoneItemDataTypeExtensionPoint;
086    
087    /**
088     * Constructor.
089     * @param root the root page.
090     * @param syncContent the synchronized content
091     * @param path the path
092     * @param resolver the {@link AmetysObjectResolver}.
093     * @param userDirectoryPageHandler the user directory page handler
094     * @param syncContentsCollectionDAO The DAO for synchronizable collections
095     * @param skinsManager the skins manager
096     * @param pageDataTypeExtensionPoint the extension point with available data types for pages
097     * @param zoneDataTypeExtensionPoint the extension point with available data types for zones
098     * @param serviceExtensionPoint the service extension point
099     * @param zoneItemDataTypeExtensionPoint the extension point with available data types for zone items
100     */
101    public UserPage(Page root, Content syncContent, String path, AmetysObjectResolver resolver, UserDirectoryPageHandler userDirectoryPageHandler, SynchronizableContentsCollectionDAO syncContentsCollectionDAO, SkinsManager skinsManager, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> pageDataTypeExtensionPoint, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> zoneDataTypeExtensionPoint, ServiceExtensionPoint serviceExtensionPoint, AbstractThreadSafeComponentExtensionPoint<RepositoryModelItemType> zoneItemDataTypeExtensionPoint)
102    {
103        _root = root;
104        _path = path;
105        _resolver = resolver;
106        _userDirectoryPageHandler = userDirectoryPageHandler;
107        _syncContentsCollectionDAO = syncContentsCollectionDAO;
108        _skinsManager = skinsManager;
109        _syncContent = syncContent;
110        
111        _title = _syncContent.getTitle(new Locale(root.getSitemapName()));
112        _initialDepth = _userDirectoryPageHandler.getDepth(_root);
113
114        _pageDataTypeExtensionPoint = pageDataTypeExtensionPoint;
115        _zoneDataTypeExtensionPoint = zoneDataTypeExtensionPoint;
116        _serviceExtensionPoint = serviceExtensionPoint;
117        _zoneItemDataTypeExtensionPoint = zoneItemDataTypeExtensionPoint;
118    }
119
120    /**
121     * Compute a page id
122     * @param path The path
123     * @param rootId The root page id
124     * @param contentId The content id
125     * @return The id
126     */
127    public static String getId(String path, String rootId, String contentId)
128    {
129        // E.g: uduser://path?rootId=...&contentId=...
130        return "uduser://" + (StringUtils.isEmpty(path) ? "_root" : path) + "?rootId=" + rootId + "&contentId=" + contentId;
131    }
132    
133    /**
134     * Returns the associated synchronizable {@link Content}.
135     * @return the associated synchronizable {@link Content}.
136     */
137    public Content getSyncContent()
138    {
139        return _syncContent;
140    }
141    
142    
143    public int getDepth() throws AmetysRepositoryException
144    {
145        return _root.getDepth() + _initialDepth + 1;
146    }
147
148    
149    public Set<String> getReferers() throws AmetysRepositoryException
150    {
151        return null;
152    }
153
154    
155    public ResourceCollection getRootAttachments() throws AmetysRepositoryException
156    {
157        return null;
158    }
159
160    
161    public String getTemplate() throws AmetysRepositoryException
162    {
163        Skin skin = _skinsManager.getSkin(getSite().getSkinId());
164        
165        if (skin.getTemplate(__USER_PAGE_TEMPLATE) != null)
166        {
167            return __USER_PAGE_TEMPLATE;
168        }
169        
170        return "page"; 
171    }
172
173    
174    public String getTitle() throws AmetysRepositoryException
175    {
176        return _title;
177    }
178    
179    
180    public String getLongTitle() throws AmetysRepositoryException
181    {
182        return _title;
183    }
184
185    
186    public PageType getType() throws AmetysRepositoryException
187    {
188        return PageType.CONTAINER;
189    }
190
191    
192    public String getURL() throws AmetysRepositoryException
193    {
194        throw new UnsupportedOperationException("#getURL is not supported on virtual user pages");
195    }
196
197    
198    public LinkType getURLType() throws AmetysRepositoryException
199    {
200        throw new UnsupportedOperationException("#getURLType is not supported on virtual user pages");
201    }
202
203    
204    public Zone getZone(String name) throws UnknownZoneException, AmetysRepositoryException
205    {
206        if (!"default".equals(name))
207        {
208            throw new IllegalArgumentException("Only the zone named 'default' is actually supported on virtual user pages.");
209        }
210        
211        return new UserZone(this, _zoneDataTypeExtensionPoint, _zoneItemDataTypeExtensionPoint);
212    }
213
214    
215    public AmetysObjectIterable< ? extends Zone> getZones() throws AmetysRepositoryException
216    {
217        ArrayList<Zone> zones = new ArrayList<>();
218        zones.add(new UserZone(this, _zoneDataTypeExtensionPoint, _zoneItemDataTypeExtensionPoint));
219        return new CollectionIterable<>(zones);
220    }
221
222    
223    public boolean hasZone(String name) throws AmetysRepositoryException
224    {
225        return "default".equals(name);
226    }
227
228    
229    public AmetysObjectIterable<? extends Page> getChildrenPages() throws AmetysRepositoryException
230    {
231        ArrayList<Page> children = new ArrayList<>();
232        return new CollectionIterable<>(children);
233    }
234
235    
236    public String getPathInSitemap() throws AmetysRepositoryException
237    {
238        if (_path.equals("_root"))
239        {
240            return _root.getPathInSitemap() + "/" + getName();
241        }
242        else
243        {
244            String path = StringUtils.lowerCase(_path);
245            return _root.getPathInSitemap() + "/" + path + "/" + getName();
246        }
247    }
248
249    
250    public Site getSite() throws AmetysRepositoryException
251    {
252        return _root.getSite();
253    }
254
255    
256    public String getSiteName() throws AmetysRepositoryException
257    {
258        return _root.getSiteName();
259    }
260
261    
262    public Sitemap getSitemap() throws AmetysRepositoryException
263    {
264        return _root.getSitemap();
265    }
266
267    
268    public String getSitemapName() throws AmetysRepositoryException
269    {
270        return _root.getSitemapName();
271    }
272
273    
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        return null;
282    }
283
284    @SuppressWarnings("unchecked")
285    
286    public AmetysObjectIterable<? extends AmetysObject> getChildren() throws AmetysRepositoryException
287    {
288        return getChildrenPages();
289    }
290
291    
292    public boolean hasChild(String name) throws AmetysRepositoryException
293    {
294        return false;
295    }
296    
297    
298    public String getId() throws AmetysRepositoryException
299    {
300        return getId(_path, _root.getId(), _syncContent.getId());
301    }
302
303    
304    public String getName() throws AmetysRepositoryException
305    {
306        return _syncContent.getName();
307    }
308    
309    @SuppressWarnings("unchecked")
310    
311    public Page getParent() throws AmetysRepositoryException
312    {
313        if (_initialDepth > 0)
314        {
315            String pathName = StringUtils.substringAfterLast(_path, "/");
316            String name = _userDirectoryPageHandler.getName(pathName);
317            return new TransitionalPage(_root, name, _path, _resolver, _userDirectoryPageHandler, _syncContentsCollectionDAO, _skinsManager, _pageDataTypeExtensionPoint, _zoneDataTypeExtensionPoint, _serviceExtensionPoint, _zoneItemDataTypeExtensionPoint);
318        }
319        else
320        {
321            return _root;
322        }
323    }
324
325    
326    public String getParentPath() throws AmetysRepositoryException
327    {
328        if (_initialDepth > 0)
329        {
330            String path = StringUtils.lowerCase(_path);
331            return _root.getPath() + "/" + path;
332        }
333        else
334        {
335            return _root.getPath();
336        }
337    }
338
339    
340    public String getPath() throws AmetysRepositoryException
341    {
342        return getParentPath() + "/" + getName();
343    }
344
345    public ModelLessDataHolder getDataHolder()
346    {
347        RepositoryData repositoryData = new MemoryRepositoryData(getName());
348        return new DefaultModelLessDataHolder(_pageDataTypeExtensionPoint, repositoryData);
349    }
350
351    
352    public Set<String> getTags() throws AmetysRepositoryException
353    {
354        return Collections.emptySet();
355    }
356
357    
358    public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePage) throws AmetysRepositoryException
359    {
360        ArrayList<Page> children = new ArrayList<>();
361        return new CollectionIterable<>(children);
362    }
363
364    
365    public boolean isVisible() throws AmetysRepositoryException
366    {
367        return false;
368    }
369
370    
371    public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException
372    {
373        throw new UnknownAmetysObjectException("There is no child for user page");
374    }
375
376    public Map<AnonymousOrAnyConnectedKeys, Set<String>> getProfilesForAnonymousAndAnyConnectedUser()
377    {
378        List<String> collectionIds = _getCollectionIds();
379        for (String collectionId : collectionIds)
380        {
381            SynchronizableContentsCollection collection = _syncContentsCollectionDAO.getSynchronizableContentsCollection(collectionId);
382            String restrictedField = collection.getRestrictedField();
383            if (!StringUtils.isEmpty(restrictedField))
384            {
385                Boolean restricted = _syncContent.getValue(restrictedField);
386                if (restricted != null && restricted.booleanValue())
387                {
388                    return Map.of(AnonymousOrAnyConnectedKeys.ANONYMOUS_DENIED, Set.of(RightManager.READER_PROFILE_ID));
389                }
390            }
391        }
392        
393        return Map.of();
394    }
395    
396    public Map<GroupIdentity, Map<UserOrGroup, Set<String>>> getProfilesForGroups(Set<GroupIdentity> groups)
397    {
398        return Map.of();
399    }
400    
401    public Map<UserIdentity, Map<UserOrGroup, Set<String>>> getProfilesForUsers(UserIdentity user)
402    {
403        return Map.of();
404    }
405    
406    public boolean isInheritanceDisallowed()
407    {
408        return false;
409    }
410    
411    private List<String> _getCollectionIds() throws AmetysRepositoryException
412    {
413        List<String> collectionIds = new ArrayList<>();
414        
415        if (_syncContent instanceof DefaultContent)
416        {
417            try
418            {
419                Node node = ((DefaultContent) _syncContent).getNode();
420                if (node.hasProperty(SynchronizableContentsCollection.COLLECTION_ID_PROPERTY))
421                {
422                    Value[] values = node.getProperty(SynchronizableContentsCollection.COLLECTION_ID_PROPERTY).getValues();
423                    for (Value value : values)
424                    {
425                        collectionIds.add(value.getString());
426                    }
427                }
428            }
429            catch (RepositoryException e)
430            {
431                throw new AmetysRepositoryException("Failed to get linked synchronizable collections for content " + _syncContent.getId(), e);
432            }
433        }
434        
435        return collectionIds;
436    }
437    
438    public ModelAwareDataHolder getTemplateParametersHolder() throws AmetysRepositoryException
439    {
440        return null;
441    }
442}