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