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