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.workspaces.documents;
017
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021import java.util.Set;
022
023import org.apache.avalon.framework.context.Context;
024import org.apache.avalon.framework.context.ContextException;
025import org.apache.cocoon.components.ContextHelper;
026import org.apache.cocoon.environment.Request;
027
028import org.ametys.core.right.RightManager.RightResult;
029import org.ametys.core.ui.Callable;
030import org.ametys.core.user.UserIdentity;
031import org.ametys.plugins.explorer.ExplorerNode;
032import org.ametys.plugins.explorer.ObservationConstants;
033import org.ametys.plugins.explorer.resources.ModifiableResourceCollection;
034import org.ametys.plugins.explorer.resources.Resource;
035import org.ametys.plugins.explorer.resources.ResourceCollection;
036import org.ametys.plugins.repository.AmetysObject;
037import org.ametys.plugins.repository.AmetysRepositoryException;
038import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
039import org.ametys.plugins.workspaces.AbstractWorkspaceModule;
040import org.ametys.plugins.workspaces.documents.jcr.FolderFactory;
041import org.ametys.plugins.workspaces.project.objects.Project;
042import org.ametys.plugins.workspaces.util.StatisticColumn;
043import org.ametys.plugins.workspaces.util.StatisticsColumnType;
044import org.ametys.runtime.i18n.I18nizableText;
045import org.ametys.web.repository.page.ModifiablePage;
046import org.ametys.web.repository.page.ModifiableZone;
047import org.ametys.web.repository.page.ModifiableZoneItem;
048import org.ametys.web.repository.page.ZoneItem.ZoneType;
049
050import com.google.common.collect.ImmutableSet;
051
052/**
053 * Helper component for managing documents
054 */
055public class DocumentWorkspaceModule extends AbstractWorkspaceModule
056{
057    /** The id of document module */
058    public static final String DOCUMENT_MODULE_ID = DocumentWorkspaceModule.class.getName();
059    
060    /** Workspaces documents node name */
061    public static final String WORKSPACES_DOCUMENTS_NODE_NAME = "documents";
062
063    private static final String __DOCUMENT_NUMBER_HEADER_ID = WORKSPACES_DOCUMENTS_NODE_NAME + "$document_number";
064    
065    @Override
066    public void contextualize(Context context) throws ContextException
067    {
068        _context = context;
069    }
070    
071    @Override
072    public String getId()
073    {
074        return DOCUMENT_MODULE_ID;
075    }
076    
077    @Override
078    public String getModuleName()
079    {
080        return WORKSPACES_DOCUMENTS_NODE_NAME;
081    }
082    
083    public int getOrder()
084    {
085        return ORDER_DOCUMENTS;
086    }
087    
088    @Override
089    protected String getModulePageName()
090    {
091        return "documents";
092    }
093    
094    public I18nizableText getModuleTitle()
095    {
096        return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_DOCUMENT_LABEL");
097    }
098    public I18nizableText getModuleDescription()
099    {
100        return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_SERVICE_MODULE_DOCUMENT_DESCRIPTION");
101    }
102    @Override
103    protected I18nizableText getModulePageTitle()
104    {
105        return new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_WORKSPACE_PAGE_DOCUMENTS_TITLE");
106    }
107    
108    @Override
109    protected void initializeModulePage(ModifiablePage documentPage)
110    {
111        ModifiableZone defaultZone = documentPage.createZone("default");
112        
113        String serviceId = "org.ametys.plugins.workspaces.module.Document";
114        ModifiableZoneItem defaultZoneItem = defaultZone.addZoneItem();
115        defaultZoneItem.setType(ZoneType.SERVICE);
116        defaultZoneItem.setServiceId(serviceId);
117        
118        ModifiableModelAwareDataHolder serviceDataHolder = defaultZoneItem.getServiceParameters();
119        serviceDataHolder.setValue("xslt", _getDefaultXslt(serviceId));
120    }
121
122    /**
123     * Get the URI of a folder in project'site
124     * @param project The project
125     * @param collectionId The id of collection
126     * @return The thread uri
127     */
128    public String getFolderUri(Project project, String collectionId)
129    {
130        String moduleUrl = getModuleUrl(project);
131        if (moduleUrl != null)
132        {
133            StringBuilder sb = new StringBuilder();
134            sb.append(moduleUrl);
135            sb.append("#").append(collectionId);
136            
137            return sb.toString();
138        }
139        
140        return null;
141    }
142    
143    /**
144     * Retrieves the set of general rights used in the document module for the current user
145     * @return The map of right data. Keys are the rights id, and values indicates whether the current user has the right or not.
146     */
147    @Callable (rights = Callable.NO_CHECK_REQUIRED)
148    public Map<String, Object> getModuleBaseRights()
149    {
150        Request request = ContextHelper.getRequest(_context);
151        
152        String projectName = (String) request.getAttribute("projectName");
153        Project project = _projectManager.getProject(projectName);
154        
155        ModifiableResourceCollection documentRoot = getModuleRoot(project, false);
156        
157        Map<String, Object> rightsData = new HashMap<>();
158        UserIdentity user = _currentUserProvider.getUser();
159        
160        // Add file / folder
161        rightsData.put("add-file", _rightManager.hasRight(user, "Plugin_Explorer_File_Add", documentRoot) == RightResult.RIGHT_ALLOW);
162        rightsData.put("add-folder", _rightManager.hasRight(user, "Plugin_Explorer_Folder_Add", documentRoot) == RightResult.RIGHT_ALLOW);
163        rightsData.put("add-cmis-folder", _rightManager.hasRight(user, "Plugin_Explorer_CMIS_Add", documentRoot) == RightResult.RIGHT_ALLOW);
164        
165        // Tags
166        rightsData.put("add-tag", _projectRightHelper.canAddTag(project));
167        rightsData.put("remove-tag", _projectRightHelper.canRemoveTag(project));
168        
169        return rightsData;
170    }
171
172    @Override
173    public Set<String> getAllowedEventTypes()
174    {
175        return ImmutableSet.of(ObservationConstants.EVENT_RESOURCE_CREATED,
176                org.ametys.plugins.workspaces.ObservationConstants.EVENT_RESOURCE_COMMENTED,
177                ObservationConstants.EVENT_RESOURCE_RENAMED,
178                ObservationConstants.EVENT_RESOURCE_UPDATED);
179    }
180
181    @Override
182    public Map<String, Object> _getInternalStatistics(Project project, boolean isActive)
183    {
184        if (isActive)
185        {
186            ModifiableResourceCollection documentRoot = getModuleRoot(project, false);
187            long fileNumber = documentRoot == null ? 0 : documentRoot.getChildren()
188                    .stream()
189                    .mapToLong(child -> _getFileNumber(child))
190                    .sum();
191            return Map.of(__DOCUMENT_NUMBER_HEADER_ID, fileNumber);
192        }
193        else
194        {
195            return Map.of(__DOCUMENT_NUMBER_HEADER_ID, __SIZE_INACTIVE);
196        }
197    }
198
199    /**
200     * Get the size of a resource (file or folder)
201     * @param document The resource
202     * @return The size in bytes
203     */
204    public long getRessourceSize(AmetysObject document)
205    {
206        if (document instanceof Resource)
207        {
208            Resource file = (Resource) document;
209            return file.getLength();
210        }
211        else if (document instanceof ResourceCollection)
212        {
213            ResourceCollection folder = (ResourceCollection) document;
214            return folder.getChildren()
215                .stream()
216                .mapToLong(child -> getRessourceSize(child))
217                .sum();
218        }
219        return 0;
220    }
221    
222    private long _getFileNumber(AmetysObject document)
223    {
224        if (document instanceof Resource)
225        {
226            return 1;
227        }
228        else if (document instanceof ResourceCollection)
229        {
230            ResourceCollection folder = (ResourceCollection) document;
231            return folder.getChildren()
232                .stream()
233                .mapToLong(child -> _getFileNumber(child))
234                .sum();
235        }
236        return 0;
237    }
238
239    @Override
240    public List<StatisticColumn> _getInternalStatisticModel()
241    {
242
243        return List.of(new StatisticColumn(__DOCUMENT_NUMBER_HEADER_ID, new I18nizableText("plugin." + _pluginName, "PLUGINS_WORKSPACES_PROJECT_STATISTICS_TOOL_COLUMN_FILE_NUMBER"))
244                .withRenderer("Ametys.plugins.workspaces.project.tool.ProjectsGridHelper.renderElements")
245                .withType(StatisticsColumnType.LONG)
246                .withGroup(GROUP_HEADER_ELEMENTS_ID));
247    }
248    
249    @Override
250    public long getModuleSize(Project project)
251    {
252        ModifiableResourceCollection collection = getModuleRoot(project, false);
253        if (collection == null)
254        {
255            return 0;
256        }
257        return getModuleRoot(project, false).getChildren()
258            .stream()
259            .mapToLong(child -> getRessourceSize(child))
260            .sum();
261    }
262
263    @Override
264    protected boolean _showModuleSize()
265    {
266        return true;
267    }
268
269    @Override
270    public Set<String> getAllEventTypes()
271    {
272        return Set.of(ObservationConstants.EVENT_RESOURCE_CREATED,
273                      org.ametys.plugins.workspaces.ObservationConstants.EVENT_RESOURCE_COMMENTED,
274                      ObservationConstants.EVENT_RESOURCE_DELETED,
275                      ObservationConstants.EVENT_RESOURCE_MOVED,
276                      ObservationConstants.EVENT_RESOURCE_RENAMED,
277                      ObservationConstants.EVENT_RESOURCE_UPDATED);
278    }
279
280    @Override
281    public ModifiableResourceCollection getModuleRoot(Project project, boolean create)
282    {
283        try
284        {
285            ExplorerNode projectRootNode = project.getExplorerRootNode();
286            
287            if (projectRootNode instanceof ModifiableResourceCollection projectRootNodeRc)
288            {
289                return _getAmetysObject(projectRootNodeRc, getModuleName(), FolderFactory.FOLDER_NODETYPE, create);
290            }
291            else
292            {
293                throw new IllegalArgumentException("Error getting the " + getModuleName() + " root node : it should be a ModifiableResourceCollection and not " + projectRootNode.getClass());
294            }
295        }
296        catch (AmetysRepositoryException e)
297        {
298            throw new AmetysRepositoryException("Error getting the " + getModuleName() + " root node.", e);
299        }
300    }
301}