001/*
002 *  Copyright 2020 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.mobileapp;
017
018import java.util.Collection;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.stream.Collectors;
023
024import org.apache.avalon.framework.component.Component;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.avalon.framework.service.Serviceable;
028import org.apache.commons.lang3.StringUtils;
029
030import org.ametys.core.util.I18nUtils;
031import org.ametys.plugins.workspaces.ObservationConstants;
032import org.ametys.plugins.workspaces.project.ProjectsCatalogueManager;
033import org.ametys.plugins.workspaces.project.objects.Project;
034import org.ametys.runtime.i18n.I18nizableText;
035import org.ametys.runtime.i18n.I18nizableTextParameter;
036import org.ametys.runtime.plugin.PluginsManager;
037import org.ametys.runtime.plugin.component.AbstractLogEnabled;
038
039/**
040 * Helper to handle project feeds
041 */
042public class FeedHelper extends AbstractLogEnabled implements Serviceable, Component
043{
044    /** Avalon Role */
045    public static final String ROLE = FeedHelper.class.getName();
046    
047    /** I18N Utils */
048    protected I18nUtils _i18nUtils;
049    
050    /** The project catalogue manager component */
051    protected ProjectsCatalogueManager _projectsCatalogueManager;
052
053    public void service(ServiceManager manager) throws ServiceException
054    {
055        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
056        if (PluginsManager.getInstance().isPluginActive("workspaces"))
057        {
058            _projectsCatalogueManager = (ProjectsCatalogueManager) manager.lookup(ProjectsCatalogueManager.ROLE);
059        }
060    }
061    
062
063    /**
064     * Generate a map of project names with the basic projects informations to return
065     * @return a map of basic information about projects
066     */
067    public Map<String, Map<String, Object>> getProjects()
068    {
069        Map<String, Map<String, Object>> result = new HashMap<>();
070        
071        if (_projectsCatalogueManager != null)
072        {
073            List<Map<String, Object>> userProjects = _projectsCatalogueManager.getUserProjects();
074            for (Map<String, Object> fullProjectMap : userProjects)
075            {
076                String name = (String) fullProjectMap.get("name");
077                Map<String, Object> neededInfos = new HashMap<>();
078    
079                neededInfos.put("name", name);
080                neededInfos.put("description", fullProjectMap.get("description"));
081                neededInfos.put("title", fullProjectMap.get("title"));
082                neededInfos.put("url", fullProjectMap.get("url"));
083                neededInfos.put("illustration", fullProjectMap.get("illustration"));
084                neededInfos.put("language", fullProjectMap.get("language"));
085                neededInfos.put("id", fullProjectMap.get("id"));
086                neededInfos.put("category", fullProjectMap.get("category"));
087               
088                result.put(name, neededInfos);
089            }
090        }
091        
092        return result;
093    }
094    
095    /**
096     * Transform a {@link Project} into a json map
097     * @param project the project to parse
098     * @return a json map
099     */
100    public Map<String, Object> projectToMap(Project project)
101    {
102        return _projectsCatalogueManager.detailedProject2json(project);
103    }
104    /**
105     * Add infos to the activity, so it can be displayed by the app
106     * @param activity the json representing the activity to parse
107     * @param project the project, as a json map (see {@link FeedHelper#projectToMap(Project)})
108     * @param lang language to use to translate the short description
109     * @return a map to return in json
110     */
111    public Map<String, Object> getActivityInfos(Map<String, Object> activity, Map<String, Object> project, String lang)
112    {
113        Map<String, Object> result = new HashMap<>();
114        
115        result.putAll(activity);
116        
117        result.put("project", project);
118        
119        @SuppressWarnings("unchecked")
120        Map<String, String> activityAuthor = (Map<String, String>) activity.get("author");
121        
122        Map<String, String> author = new HashMap<>();
123        author.put("fullname", activityAuthor.get("fullname"));
124        result.put("author", author);
125        
126        result.put("short-description", getActivityDescription(activity, lang));
127
128        result.put("content_id", getActivityObjectId(activity));
129        result.put("content_url", getActivityUrl(activity));
130        
131        return result;
132    }
133    
134    /**
135     * Generate a description for this activity
136     * @param activity the activity to describe
137     * @return a String of the description
138     */
139    @SuppressWarnings("unchecked")
140    protected String getActivityObjectId(Map<String, Object> activity)
141    {
142        String id = null;
143        String eventType = (String) activity.get("type");
144        
145        switch (eventType)
146        {
147            /*
148             * RESOURCES
149             */
150            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_CREATED :
151            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_UPDATED :
152            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_RENAMED :
153                if (activity.containsKey("file"))
154                {
155                    id = (String) ((Map<String, Object>) activity.get("file")).get("id");
156                }
157                break;
158
159            /*
160             * CALENDAR
161             */
162            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_CREATED :
163            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_UPDATED :
164                id = (String) activity.get("eventId");
165                break;
166
167            /*
168             * THREAD
169             */
170            case ObservationConstants.EVENT_THREAD_CREATED :
171            case ObservationConstants.EVENT_THREAD_COMMENTED :
172                id = (String) activity.get("threadId");
173                break;
174
175            /*
176             * MEMBER
177             */
178            case ObservationConstants.EVENT_MEMBER_ADDED :
179                // no id available
180                break;
181            case ObservationConstants.EVENT_WALLCONTENT_ADDED :
182                id = (String) activity.get("contentId");
183                break;
184
185            /**
186             * WIKI
187             */
188            case ObservationConstants.EVENT_MINISITE_PAGE_CREATED :
189            case ObservationConstants.EVENT_MINISITE_PAGE_UPDATED :
190            case ObservationConstants.EVENT_MINISITE_PAGE_RENAMED :
191            case ObservationConstants.EVENT_MINISITE_PAGE_DELETED :
192                id = (String) activity.get("pageId");
193                break;
194
195            /**
196             * TASK
197             */
198            case ObservationConstants.EVENT_TASK_CREATED :
199            case ObservationConstants.EVENT_TASK_ASSIGNED :
200            case ObservationConstants.EVENT_TASK_CLOSED_STATUS_CHANGED :
201                id = (String) activity.get("taskId");
202                break;
203            default:
204                break;
205        }
206        
207        return id;
208    }
209    
210
211    /**
212     * Generate a description for this activity
213     * @param activity the activity to describe
214     * @return a String of the description
215     */
216    protected String getActivityUrl(Map<String, Object> activity)
217    {
218        String url = null;
219        String eventType = (String) activity.get("type");
220        
221        switch (eventType)
222        {
223            /*
224             * RESOURCES
225             */
226            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_CREATED :
227            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_UPDATED :
228            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_RENAMED :
229                url = (String) activity.get("parentFolderUrl");
230                break;
231
232            /*
233             * CALENDAR
234             */
235            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_CREATED :
236            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_UPDATED :
237                url = (String) activity.get("eventUrl");
238                break;
239
240            /*
241             * THREAD
242             */
243            case ObservationConstants.EVENT_THREAD_CREATED :
244            case ObservationConstants.EVENT_THREAD_COMMENTED :
245                url = (String) activity.get("threadUrl");
246                break;
247
248            /*
249             * MEMBER
250             */
251            case ObservationConstants.EVENT_MEMBER_ADDED :
252                // no url available
253                break;
254            case ObservationConstants.EVENT_WALLCONTENT_ADDED :
255                // no url available
256                break;
257
258            /**
259             * WIKI
260             */
261            case ObservationConstants.EVENT_MINISITE_PAGE_CREATED :
262            case ObservationConstants.EVENT_MINISITE_PAGE_UPDATED :
263            case ObservationConstants.EVENT_MINISITE_PAGE_RENAMED :
264            case ObservationConstants.EVENT_MINISITE_PAGE_DELETED :
265                url = (String) activity.get("pageUrl");
266                break;
267
268            /**
269             * TASK
270             */
271            case ObservationConstants.EVENT_TASK_CREATED :
272            case ObservationConstants.EVENT_TASK_ASSIGNED :
273            case ObservationConstants.EVENT_TASK_CLOSED_STATUS_CHANGED :
274                url = (String) activity.get("taskUrl");
275                break;
276            default:
277                break;
278        }
279        if (StringUtils.isBlank(url))
280        {
281            url = (String) activity.get("projectUrl");
282        }
283        return url;
284    }
285
286    /**
287     * Generate a description for this activity
288     * @param activity the activity to describe
289     * @param lang the language to use
290     * @return a String of the description
291     */
292    @SuppressWarnings("unchecked")
293    protected String getActivityDescription(Map<String, Object> activity, String lang)
294    {
295        String description = null;
296        
297        String eventType = (String) activity.get("type");
298        
299        Integer amount = (Integer) activity.get("amount");
300        if (amount == null)
301        {
302            amount = 1;
303        }
304        
305        String i18nKey = null;
306        Map<String, I18nizableTextParameter> parameters = new HashMap<>();
307        parameters.put("author", new I18nizableText(((Map<String, String>) activity.get("author")).get("fullname")));
308        parameters.put("project", new I18nizableText((String) activity.get("projectTitle")));
309        parameters.put("nb", new I18nizableText(amount.toString()));
310        
311        switch (eventType)
312        {
313
314            /*
315             * RESOURCES
316             */
317            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_CREATED :
318                parameters.put("folder", new I18nizableText((String) activity.get("parentFolder")));
319                if (amount == 1)
320                {
321                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_ADDED_DESC";
322                }
323                else
324                {
325                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_ADDED_MULTI_DESC";
326                }
327                break;
328            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_UPDATED :
329                parameters.put("folder", new I18nizableText((String) activity.get("parentFolder")));
330                if (amount == 1)
331                {
332                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_UPDATED_DESC";
333                }
334                else
335                {
336                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_UPDATED_MULTI_DESC";
337                }
338                break;
339            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_RENAMED :
340                parameters.put("folder", new I18nizableText((String) activity.get("parentFolder")));
341                if (amount == 1)
342                {
343                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_RENAMED_DESC";
344                }
345                else
346                {
347                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_RENAMED_MULTI_DESC";
348                }
349                break;
350
351            /*
352             * CALENDAR
353             */
354            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_CREATED :
355                parameters.put("calendar", new I18nizableText((String) activity.get("calendarTitle")));
356                if (amount == 1)
357                {
358                    parameters.put("event", new I18nizableText(getTitles(activity, "events", "eventTitle")));
359                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_ADDED_DESC";
360                }
361                else
362                {
363                    parameters.put("events", new I18nizableText(getTitles(activity, "events", "eventTitle")));
364                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_ADDED_MULTI_DESC";
365                }
366                break;
367            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_UPDATED :
368                parameters.put("calendar", new I18nizableText((String) activity.get("calendarTitle")));
369                if (amount == 1)
370                {
371                    parameters.put("event", new I18nizableText(getTitles(activity, "events", "eventTitle")));
372                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_UPDATED_DESC";
373                }
374                else
375                {
376                    parameters.put("events", new I18nizableText(getTitles(activity, "events", "eventTitle")));
377                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_UPDATED_MULTI_DESC";
378                }
379                break;
380
381            /*
382             * THREAD
383             */
384            case ObservationConstants.EVENT_THREAD_CREATED :
385                if (amount == 1)
386                {
387                    parameters.put("thread", new I18nizableText(getTitles(activity, "threads", "threadTitle")));
388                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_THREAD_CREATED_DESC";
389                }
390                else
391                {
392                    parameters.put("threads", new I18nizableText(getTitles(activity, "threads", "threadTitle")));
393                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_THREAD_CREATED_MULTI_DESC";
394                }
395                break;
396            case ObservationConstants.EVENT_THREAD_COMMENTED :
397                parameters.put("thread", new I18nizableText(getTitles(activity, "threads", "threadTitle")));
398                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_THREAD_POST_CREATED_DESC";
399                break;
400
401            /*
402             * MEMBER
403             */
404            case ObservationConstants.EVENT_MEMBER_ADDED :
405                if (amount == 1)
406                {
407                    parameters.put("member", new I18nizableText((String) activity.get("member")));
408                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_MEMBER_ADDED_DESC";
409                }
410                else
411                {
412                    Collection<Map<String, Object>> members = (Collection<Map<String, Object>>) activity.get("members");
413                    List<String> names = members.stream().map(member -> (String) member.get("name")).collect(Collectors.toList());
414                    parameters.put("members", new I18nizableText(String.join(", ", names)));
415                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_MEMBERS_ADDED_DESC";
416                }
417                break;
418                
419            /**
420             * WIKI
421             */
422            case ObservationConstants.EVENT_MINISITE_PAGE_CREATED :
423                if (amount == 1)
424                {
425                    parameters.put("page", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
426                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGE_CREATED_DESC";
427                }
428                else
429                {
430                    parameters.put("pages", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
431                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGES_CREATED_DESC";
432                }
433                break;
434            case ObservationConstants.EVENT_MINISITE_PAGE_UPDATED :
435                if (amount == 1)
436                {
437                    parameters.put("page", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
438                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGE_UPDATED_DESC";
439                }
440                else
441                {
442                    parameters.put("pages", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
443                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGES_UPDATED_DESC";
444                }
445                break;
446            case ObservationConstants.EVENT_MINISITE_PAGE_RENAMED :
447                parameters.put("oldTitle", new I18nizableText((String) activity.get("pageOldTitle")));
448                parameters.put("title", new I18nizableText((String) activity.get("pageTitle")));
449                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGE_RENAMED_DESC";
450                break;
451            case ObservationConstants.EVENT_MINISITE_PAGE_DELETED :
452                if (amount == 1)
453                {
454                    parameters.put("page", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
455                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGE_DELETED_DESC";
456                }
457                else
458                {
459                    parameters.put("pages", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
460                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGES_DELETED_DESC";
461                }
462                break;
463                
464
465            /**
466             * NEWS
467             */
468            case ObservationConstants.EVENT_PROJECT_NEWS_PUBLISHED:
469                parameters.put("contentTitle", new I18nizableText((String) activity.get("contentTitle")));
470                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PROJECT_NEWS_ADDED_DESC";
471                break;
472
473            /**
474             * WALLCONTENT
475             */
476            case ObservationConstants.EVENT_WALLCONTENT_ADDED :
477                parameters.put("contentSummary", new I18nizableText((String) activity.get("contentSummary")));
478                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_WALLCONTENT_ADDED_WITH_TITLE_DESC";
479                break;
480                
481            /**
482             * NEWS AND WALLCONTENTS COMMENTS
483             */
484            case org.ametys.cms.ObservationConstants.EVENT_CONTENT_COMMENT_VALIDATED :
485                parameters.put("contentTitle", new I18nizableText((String) activity.get("contentTitle")));
486                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CONTENT_COMMENT_VALIDATED_DESC";
487                break;
488
489            /**
490             * TASK
491             */
492            case ObservationConstants.EVENT_TASK_CREATED :
493                if (amount == 1)
494                {
495                    parameters.put("task", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
496                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_ADDED_DESC";
497                }
498                else
499                {
500                    parameters.put("tasks", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
501                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASKS_ADDED_DESC";
502                }
503                break;
504            case ObservationConstants.EVENT_TASK_DELETING :
505                if (amount == 1)
506                {
507                    parameters.put("task", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
508                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_DELETED_DESC";
509                }
510                else
511                {
512                    parameters.put("tasks", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
513                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASKS_DELETED_DESC";
514                }
515                break;
516            case ObservationConstants.EVENT_TASK_ASSIGNED :
517                parameters.put("task", new I18nizableText((String) activity.get("taskTitle")));
518                parameters.put("assignee", new I18nizableText((String) activity.get("assignees")));
519                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_ASSIGNED_DESC";
520                break;
521            case ObservationConstants.EVENT_TASK_CLOSED_STATUS_CHANGED :
522                parameters.put("task", new I18nizableText((String) activity.get("taskTitle")));
523                
524                boolean isClosed = "true".equals(activity.get("isClosed"));
525                i18nKey = isClosed ? "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_CLOSED_STATUS_CLOSE_DESC" : "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_CLOSED_STATUS_OPEN_DESC";
526                break;
527            default:
528                break;
529        }
530        
531        if (i18nKey != null)
532        {
533            I18nizableText descriptionI18n = new I18nizableText("plugin.workspaces", i18nKey, parameters);
534            description = _i18nUtils.translate(descriptionI18n, lang);
535        }
536        
537        return description;
538    }
539    
540    /**
541     * Aggregates a list of elements from the activity.
542     * activity = {
543     *      mapKey : [
544     *          {
545     *              titleKey : "Title 1",
546     *              …
547     *          },
548     *          {
549     *              titleKey : "Title 2",
550     *              …
551     *          },
552     *          {
553     *              titleKey : "Title 3",
554     *              …
555     *          }
556     *      ],
557     *      …
558     *  }
559     * @param activity the activity to use
560     * @param mapKey the key of the list to use
561     * @param titleKey title to use from the map
562     * @return a list, separated with commas, between each titles
563     */
564    protected String getTitles(Map<String, Object> activity, String mapKey, String titleKey)
565    {
566        @SuppressWarnings("unchecked")
567        List<Map<String, String>> items = (List<Map<String, String>>) activity.get(mapKey);
568        
569        
570        if (items == null)
571        {
572            return (String) activity.get(titleKey);
573        }
574        else
575        {
576            String result = null;
577            
578            for (Map<String, String> item : items)
579            {
580                if (result == null)
581                {
582                    result = item.get(titleKey);
583                }
584                else
585                {
586                    result += ", " + item.get(titleKey);
587                }
588            }
589            
590            return result;
591        }
592    }
593
594}