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        if (amount == null)
290        {
291            amount = 1;
292        }
293        
294        String i18nKey = null;
295        Map<String, I18nizableTextParameter> parameters = new HashMap<>();
296        parameters.put("author", new I18nizableText(((Map<String, String>) activity.get("author")).get("fullname")));
297        parameters.put("project", new I18nizableText((String) activity.get("projectTitle")));
298        parameters.put("nb", new I18nizableText(amount.toString()));
299        
300        switch (eventType)
301        {
302
303            /*
304             * RESOURCES
305             */
306            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_CREATED :
307                parameters.put("folder", new I18nizableText((String) activity.get("parentFolder")));
308                if (amount == 1)
309                {
310                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_ADDED_DESC";
311                }
312                else
313                {
314                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_ADDED_MULTI_DESC";
315                }
316                break;
317            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_UPDATED :
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_UPDATED_DESC";
322                }
323                else
324                {
325                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_UPDATED_MULTI_DESC";
326                }
327                break;
328            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_RENAMED :
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_RENAMED_DESC";
333                }
334                else
335                {
336                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_RENAMED_MULTI_DESC";
337                }
338                break;
339
340            /*
341             * CALENDAR
342             */
343            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_CREATED :
344                parameters.put("calendar", new I18nizableText((String) activity.get("calendarTitle")));
345                if (amount == 1)
346                {
347                    parameters.put("event", new I18nizableText(getTitles(activity, "events", "eventTitle")));
348                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_ADDED_DESC";
349                }
350                else
351                {
352                    parameters.put("events", new I18nizableText(getTitles(activity, "events", "eventTitle")));
353                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_ADDED_MULTI_DESC";
354                }
355                break;
356            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_UPDATED :
357                parameters.put("calendar", new I18nizableText((String) activity.get("calendarTitle")));
358                if (amount == 1)
359                {
360                    parameters.put("event", new I18nizableText(getTitles(activity, "events", "eventTitle")));
361                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_UPDATED_DESC";
362                }
363                else
364                {
365                    parameters.put("events", new I18nizableText(getTitles(activity, "events", "eventTitle")));
366                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_UPDATED_MULTI_DESC";
367                }
368                break;
369
370            /*
371             * THREAD
372             */
373            case org.ametys.plugins.explorer.ObservationConstants.EVENT_THREAD_CREATED :
374                if (amount == 1)
375                {
376                    parameters.put("thread", new I18nizableText(getTitles(activity, "threads", "threadTitle")));
377                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_THREAD_CREATED_DESC";
378                }
379                else
380                {
381                    parameters.put("threads", new I18nizableText(getTitles(activity, "threads", "threadTitle")));
382                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_THREAD_CREATED_MULTI_DESC";
383                }
384                break;
385            case org.ametys.plugins.explorer.ObservationConstants.EVENT_THREAD_POST_CREATED :
386                parameters.put("thread", new I18nizableText(getTitles(activity, "threads", "threadTitle")));
387                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_THREAD_POST_CREATED_DESC";
388                break;
389
390            /*
391             * MEMBER
392             */
393            case ObservationConstants.EVENT_MEMBER_ADDED :
394                if (amount == 1)
395                {
396                    parameters.put("member", new I18nizableText((String) activity.get("member")));
397                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_MEMBER_ADDED_DESC";
398                }
399                else
400                {
401                    Collection<Map<String, Object>> members = (Collection<Map<String, Object>>) activity.get("members");
402                    List<String> names = members.stream().map(member -> (String) member.get("name")).collect(Collectors.toList());
403                    parameters.put("members", new I18nizableText(String.join(", ", names)));
404                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_MEMBERS_ADDED_DESC";
405                }
406                break;
407            case ObservationConstants.EVENT_WALLCONTENT_ADDED :
408                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_WALLCONTENT_ADDED_DESC";
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             * TASK
458             */
459            case ObservationConstants.EVENT_TASK_CREATED :
460                if (amount == 1)
461                {
462                    parameters.put("task", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
463                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_ADDED_DESC";
464                }
465                else
466                {
467                    parameters.put("tasks", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
468                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASKS_ADDED_DESC";
469                }
470                break;
471            case ObservationConstants.EVENT_TASK_ASSIGNED :
472                parameters.put("task", new I18nizableText((String) activity.get("taskTitle")));
473                parameters.put("assignee", new I18nizableText((String) activity.get("assignees")));
474                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_ASSIGNED_DESC";
475                break;
476            case ObservationConstants.EVENT_TASK_CLOSED_STATUS_CHANGED :
477                parameters.put("task", new I18nizableText((String) activity.get("taskTitle")));
478                
479                boolean isClosed = "true".equals(activity.get("isClosed"));
480                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";
481                break;
482            default:
483                break;
484        }
485        
486        if (i18nKey != null)
487        {
488            I18nizableText descriptionI18n = new I18nizableText("plugin.workspaces", i18nKey, parameters);
489            description = _i18nUtils.translate(descriptionI18n, lang);
490        }
491        
492        return description;
493    }
494    
495    /**
496     * Aggregates a list of elements from the activity.
497     * activity = {
498     *      mapKey : [
499     *          {
500     *              titleKey : "Title 1",
501     *              …
502     *          },
503     *          {
504     *              titleKey : "Title 2",
505     *              …
506     *          },
507     *          {
508     *              titleKey : "Title 3",
509     *              …
510     *          }
511     *      ],
512     *      …
513     *  }
514     * @param activity the activity to use
515     * @param mapKey the key of the list to use
516     * @param titleKey title to use from the map
517     * @return a list, separated with commas, between each titles
518     */
519    protected String getTitles(Map<String, Object> activity, String mapKey, String titleKey)
520    {
521        @SuppressWarnings("unchecked")
522        List<Map<String, String>> items = (List<Map<String, String>>) activity.get(mapKey);
523        
524        
525        if (items == null)
526        {
527            return (String) activity.get(titleKey);
528        }
529        else
530        {
531            String result = null;
532            
533            for (Map<String, String> item : items)
534            {
535                if (result == null)
536                {
537                    result = item.get(titleKey);
538                }
539                else
540                {
541                    result += ", " + item.get(titleKey);
542                }
543            }
544            
545            return result;
546        }
547    }
548
549}