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.activities.documents;
017
018import java.util.ArrayList;
019import java.util.List;
020import java.util.Map;
021import java.util.stream.Stream;
022
023import javax.jcr.RepositoryException;
024
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027
028import org.ametys.core.observation.Event;
029import org.ametys.core.util.I18nUtils;
030import org.ametys.plugins.explorer.ObservationConstants;
031import org.ametys.plugins.explorer.resources.Resource;
032import org.ametys.plugins.explorer.resources.ResourceCollection;
033import org.ametys.plugins.repository.AmetysObject;
034import org.ametys.plugins.repository.AmetysObjectResolver;
035import org.ametys.plugins.repository.activities.Activity;
036import org.ametys.plugins.repository.activities.ActivityType;
037import org.ametys.plugins.repository.query.expression.Expression;
038import org.ametys.plugins.repository.query.expression.Expression.Operator;
039import org.ametys.plugins.repository.query.expression.ExpressionContext;
040import org.ametys.plugins.repository.query.expression.OrExpression;
041import org.ametys.plugins.repository.query.expression.StringExpression;
042import org.ametys.plugins.workspaces.activities.AbstractWorkspacesActivityType;
043import org.ametys.plugins.workspaces.activities.documents.AbstractResourceReferenceElementType.ResourceReference;
044import org.ametys.plugins.workspaces.documents.DocumentWorkspaceModule;
045import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint;
046import org.ametys.plugins.workspaces.project.notification.ResourceNotifierHelper;
047import org.ametys.plugins.workspaces.project.objects.Project;
048import org.ametys.runtime.i18n.I18nizableText;
049
050/**
051 * {@link ActivityType} implementation for documents-related activities
052 */
053public class DocumentsActivityType extends AbstractWorkspacesActivityType
054{
055    /** data name for the parent folder path in explorer */
056    public static final String PARENT_FOLDER_PATH = "parentPath";
057
058    /** data name for the document parent folder id */
059    public static final String PARENT_FOLDER_ID = "parentFolderId";
060
061    /** data name for the document parent folder name */
062    public static final String PARENT_FOLDER = "parentFolder";
063
064    /** data name for the activity file */
065    public static final String FILE_DATA_NAME = "file";
066    
067    /** data name for the activity files */
068    public static final String FILES_DATA_NAME = "files";
069
070    /** Constant for event's category */
071    public static final String EVENT_CATEGORY_DOCUMENTS = "documents";
072    
073    /** Resolver for Ametys objects */
074    protected AmetysObjectResolver _ametysObjectResolver;
075    
076    private I18nUtils _i18nUtils;
077
078    private DocumentWorkspaceModule _documentModule;
079
080    private ResourceNotifierHelper _resourceNotifierHelper;
081
082    
083    @Override
084    public void service(ServiceManager serviceManager) throws ServiceException
085    {
086        super.service(serviceManager);
087        _ametysObjectResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
088        _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE);
089        WorkspaceModuleExtensionPoint moduleManagerEP = (WorkspaceModuleExtensionPoint) serviceManager.lookup(WorkspaceModuleExtensionPoint.ROLE);
090        _documentModule = moduleManagerEP.getModule(DocumentWorkspaceModule.DOCUMENT_MODULE_ID);
091        _resourceNotifierHelper = (ResourceNotifierHelper) serviceManager.lookup(ResourceNotifierHelper.ROLE);
092    }
093    
094    @Override
095    public boolean support(Event event)
096    {
097        return !event.getId().equals(ObservationConstants.EVENT_RESOURCE_UPDATED)
098            || _isResourceRecentlyModified(event);
099    }
100
101    private boolean _isResourceRecentlyModified(Event event)
102    {
103        String resourceId = (String) event.getArguments().getOrDefault(ObservationConstants.ARGS_ID, null);
104        return !_resourceNotifierHelper.resourceRecentlyModified(resourceId);
105    }
106    
107    @Override
108    public void setAdditionalActivityData(Activity activity, Map<String, Object> parameters) throws RepositoryException
109    {
110        super.setAdditionalActivityData(activity, parameters);
111        
112        activity.setValue(PROJECT_CATEGORY, EVENT_CATEGORY_DOCUMENTS);
113        
114        String folderId = (String) parameters.get(ObservationConstants.ARGS_PARENT_ID);
115        ResourceCollection parentFolder = null;
116        if (folderId == null)
117        {
118            String resourceId = (String) parameters.get(ObservationConstants.ARGS_ID);
119            Resource resource = _ametysObjectResolver.resolveById(resourceId);
120            parentFolder = resource.getParent();
121            folderId = parentFolder.getId();
122        }
123        else
124        {
125            parentFolder = _ametysObjectResolver.resolveById(folderId);
126        }
127        activity.setValue(PARENT_FOLDER_ID, folderId);
128        
129        // Parent name
130        String parentFolderName = parentFolder.getName();
131        if ("ametys-internal:resources".equals(parentFolderName))
132        {
133            parentFolderName = _i18nUtils.translate(new I18nizableText("plugin.explorer", "PLUGINS_EXPLORER_ROOT_NODE"));
134        }
135        activity.setValue(PARENT_FOLDER, parentFolderName);
136        
137        activity.setValue(PARENT_FOLDER_PATH, parentFolder.getExplorerPath());
138    }
139    
140    @Override
141    public Map<String, Object> additionnalDataToJSONForClient(Activity activity)
142    {
143        Map<String, Object> json = super.additionnalDataToJSONForClient(activity);
144        String parentId = activity.getValue(PARENT_FOLDER_ID);
145        
146        String projectName = activity.getValue(PROJECT_NAME);
147        Project project = _projectManager.getProject(projectName);
148        if (project != null)
149        {
150            json.put("parentFolderUrl", _documentModule.getFolderUri(project, parentId));
151        }
152        return json;
153    }
154    
155    @Override
156    public boolean isMergeable(Activity activity1, Activity activity2)
157    {
158        if (!super.isMergeable(activity1, activity2))
159        {
160            return false;
161        }
162        String parentFolder1 = activity1.getValue(PARENT_FOLDER_ID);
163        String parentFolder2 = activity2.getValue(PARENT_FOLDER_ID);
164        
165        return parentFolder1 != null && parentFolder2 != null && parentFolder1.equals(parentFolder2);
166    }
167    
168    @Override
169    public Expression getFilterPatternExpression(String pattern)
170    {
171        Expression folderExpr = new StringExpression(PARENT_FOLDER, Operator.WD, pattern, ExpressionContext.newInstance().withCaseInsensitive(true));
172        Expression filesExpr = new StringExpression(FILES_DATA_NAME + "/*/name", Operator.WD, pattern, ExpressionContext.newInstance().withCaseInsensitive(true));
173        Expression fileExpr = new StringExpression(FILE_DATA_NAME + "/name", Operator.WD, pattern, ExpressionContext.newInstance().withCaseInsensitive(true));
174        
175        return new OrExpression(folderExpr, filesExpr, fileExpr);
176    }
177    
178    @SuppressWarnings("unchecked")
179    @Override
180    public Map<String, Object> mergeActivities(List<Activity> activities)
181    {
182        Map<String, Object> mergedActivities = super.mergeActivities(activities);
183        
184        List<Map<String, Object>> files = new ArrayList<>();
185        
186        List<String> knownFiles = new ArrayList<>();
187        
188        for (Activity activity : activities)
189        {
190            Map<String, Object> json = activity.toJSONForClient();
191            if (json.containsKey(FILE_DATA_NAME))
192            {
193                Map<String, Object> file = (Map<String, Object>) json.get(FILE_DATA_NAME);
194                if (!knownFiles.contains(file.get("id")))
195                {
196                    files.add((Map<String, Object>) json.get(FILE_DATA_NAME));
197                    knownFiles.add((String) file.get("id"));
198                }
199            }
200            else if (json.containsKey(FILES_DATA_NAME))
201            {
202                files.addAll((List<Map<String, Object>>) json.get("files"));
203            }
204        }
205        
206        if (files.size() > 0)
207        {
208            mergedActivities.put("files", files);
209        }
210        
211        mergedActivities.put("amount", files.size());
212        
213        return mergedActivities;
214    }
215    
216    @Override
217    public Project getProjectFromEvent(Event event)
218    {
219        Map<String, Object> args = event.getArguments();
220        String parentId = (String) args.get(ObservationConstants.ARGS_PARENT_ID);
221        if (parentId != null)
222        {
223            return getParentProject(_resolver.resolveById(parentId));
224        }
225        else
226        {
227            String resourceId = (String) args.get(ObservationConstants.ARGS_ID);
228            Resource resource = _resolver.resolveById(resourceId);
229            return getParentProject(resource.getParent());
230        }
231    }
232    
233    @Override
234    public AmetysObject getTargetAmetysObject(Activity activity)
235    {
236        return _resolver.resolveById(activity.getValue(DocumentsActivityType.PARENT_FOLDER_ID));
237    }
238    @Override
239    public String getMailBodyURI(Activity activity)
240    {
241        return "cocoon://_plugins/workspaces/notification-mail-resource";
242    }
243    
244    @Override
245    public String getSubjectI18nKey(Activity activity)
246    {
247        String i18nKey = super.getSubjectI18nKey(activity);
248        if (activity.hasValue(DocumentsActivityType.FILES_DATA_NAME))
249        {
250            ResourceReference[] refs = activity.getValue(DocumentsActivityType.FILES_DATA_NAME);
251            if (refs.length > 1)
252            {
253                i18nKey += "_MULTIPLE";
254            }
255        }
256        return i18nKey;
257    }
258    
259    @Override
260    public List<String> getSubjectI18nParams(Activity activity)
261    {
262        List<String> i18nParams = super.getSubjectI18nParams(activity);
263        
264        if (activity.hasValue(DocumentsActivityType.FILE_DATA_NAME))
265        {
266            ResourceReference ref = activity.getValue(DocumentsActivityType.FILE_DATA_NAME);
267            i18nParams.add(ref.oldName() != null ? ref.oldName() : ref.name()); // {1}
268        }
269        else if (activity.hasValue(DocumentsActivityType.FILES_DATA_NAME))
270        {
271            ResourceReference[] refs = activity.getValue(DocumentsActivityType.FILES_DATA_NAME);
272            if (refs.length > 1)
273            {
274                i18nParams.add(String.valueOf(refs.length)); // {1}
275            }
276            else
277            {
278                i18nParams.add(Stream.of(refs).findFirst().map(ResourceReference::name).orElse("")); // {1}
279            }
280        }
281        return i18nParams;
282    }
283}