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            case ObservationConstants.EVENT_RESOURCE_COMMENTED :
351                parameters.put("folder", new I18nizableText((String) activity.get("parentFolder")));
352                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_COMMENTED_DESC";
353                break;
354            case org.ametys.plugins.explorer.ObservationConstants.EVENT_RESOURCE_DELETED :
355                parameters.put("folder", new I18nizableText((String) activity.get("parentFolder")));
356                if (amount == 1)
357                {
358                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_DELETED_DESC";
359                }
360                else
361                {
362                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_DOCUMENT_DELETED_MULTI_DESC";
363                }
364                break;
365
366            /*
367             * CALENDAR
368             */
369            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_CREATED :
370                parameters.put("calendar", new I18nizableText((String) activity.get("calendarTitle")));
371                if (amount == 1)
372                {
373                    parameters.put("event", new I18nizableText(getTitles(activity, "events", "eventTitle")));
374                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_ADDED_DESC";
375                }
376                else
377                {
378                    parameters.put("events", new I18nizableText(getTitles(activity, "events", "eventTitle")));
379                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_ADDED_MULTI_DESC";
380                }
381                break;
382            case org.ametys.plugins.workspaces.calendars.ObservationConstants.EVENT_CALENDAR_EVENT_UPDATED :
383                parameters.put("calendar", new I18nizableText((String) activity.get("calendarTitle")));
384                if (amount == 1)
385                {
386                    parameters.put("event", new I18nizableText(getTitles(activity, "events", "eventTitle")));
387                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_UPDATED_DESC";
388                }
389                else
390                {
391                    parameters.put("events", new I18nizableText(getTitles(activity, "events", "eventTitle")));
392                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CALENDAR_UPDATED_MULTI_DESC";
393                }
394                break;
395
396            /*
397             * THREAD
398             */
399            case ObservationConstants.EVENT_THREAD_CREATED :
400                if (amount == 1)
401                {
402                    parameters.put("thread", new I18nizableText(getTitles(activity, "threads", "threadTitle")));
403                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_THREAD_CREATED_DESC";
404                }
405                else
406                {
407                    parameters.put("threads", new I18nizableText(getTitles(activity, "threads", "threadTitle")));
408                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_THREAD_CREATED_MULTI_DESC";
409                }
410                break;
411            case ObservationConstants.EVENT_THREAD_COMMENTED :
412                parameters.put("thread", new I18nizableText(getTitles(activity, "threads", "threadTitle")));
413                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_THREAD_POST_CREATED_DESC";
414                break;
415
416            /*
417             * MEMBER
418             */
419            case ObservationConstants.EVENT_MEMBER_ADDED :
420                if (amount == 1)
421                {
422                    parameters.put("member", new I18nizableText((String) activity.get("member")));
423                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_MEMBER_ADDED_DESC";
424                }
425                else
426                {
427                    Collection<Map<String, Object>> members = (Collection<Map<String, Object>>) activity.get("members");
428                    List<String> names = members.stream().map(member -> (String) member.get("name")).collect(Collectors.toList());
429                    parameters.put("members", new I18nizableText(String.join(", ", names)));
430                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_MEMBERS_ADDED_DESC";
431                }
432                break;
433                
434            /**
435             * WIKI
436             */
437            case ObservationConstants.EVENT_MINISITE_PAGE_CREATED :
438                if (amount == 1)
439                {
440                    parameters.put("page", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
441                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGE_CREATED_DESC";
442                }
443                else
444                {
445                    parameters.put("pages", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
446                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGES_CREATED_DESC";
447                }
448                break;
449            case ObservationConstants.EVENT_MINISITE_PAGE_UPDATED :
450                if (amount == 1)
451                {
452                    parameters.put("page", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
453                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGE_UPDATED_DESC";
454                }
455                else
456                {
457                    parameters.put("pages", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
458                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGES_UPDATED_DESC";
459                }
460                break;
461            case ObservationConstants.EVENT_MINISITE_PAGE_RENAMED :
462                parameters.put("oldTitle", new I18nizableText((String) activity.get("pageOldTitle")));
463                parameters.put("title", new I18nizableText((String) activity.get("pageTitle")));
464                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGE_RENAMED_DESC";
465                break;
466            case ObservationConstants.EVENT_MINISITE_PAGE_DELETED :
467                if (amount == 1)
468                {
469                    parameters.put("page", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
470                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGE_DELETED_DESC";
471                }
472                else
473                {
474                    parameters.put("pages", new I18nizableText(getTitles(activity, "pages", "pageTitle")));
475                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PAGES_DELETED_DESC";
476                }
477                break;
478                
479
480            /**
481             * NEWS
482             */
483            case ObservationConstants.EVENT_PROJECT_NEWS_PUBLISHED:
484                parameters.put("contentTitle", new I18nizableText((String) activity.get("contentTitle")));
485                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_PROJECT_NEWS_ADDED_DESC";
486                break;
487
488            /**
489             * WALLCONTENT
490             */
491            case ObservationConstants.EVENT_WALLCONTENT_ADDED :
492                parameters.put("contentSummary", new I18nizableText((String) activity.get("contentSummary")));
493                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_WALLCONTENT_ADDED_WITH_TITLE_DESC";
494                break;
495                
496            /**
497             * NEWS AND WALLCONTENTS COMMENTS
498             */
499            case org.ametys.cms.ObservationConstants.EVENT_CONTENT_COMMENT_VALIDATED :
500                parameters.put("contentTitle", new I18nizableText((String) activity.get("contentTitle")));
501                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_CONTENT_COMMENT_VALIDATED_DESC";
502                break;
503
504            /**
505             * TASK
506             */
507            case ObservationConstants.EVENT_TASK_CREATED :
508                if (amount == 1)
509                {
510                    parameters.put("task", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
511                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_ADDED_DESC";
512                }
513                else
514                {
515                    parameters.put("tasks", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
516                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASKS_ADDED_DESC";
517                }
518                break;
519            case ObservationConstants.EVENT_TASK_DELETING :
520                if (amount == 1)
521                {
522                    parameters.put("task", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
523                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_DELETED_DESC";
524                }
525                else
526                {
527                    parameters.put("tasks", new I18nizableText(getTitles(activity, "tasks", "taskTitle")));
528                    i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASKS_DELETED_DESC";
529                }
530                break;
531            case ObservationConstants.EVENT_TASK_ASSIGNED :
532                parameters.put("task", new I18nizableText((String) activity.get("taskTitle")));
533                parameters.put("assignee", new I18nizableText((String) activity.get("assignees")));
534                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_ASSIGNED_DESC";
535                break;
536            case ObservationConstants.EVENT_TASK_COMMENTED :
537                parameters.put("task", new I18nizableText((String) activity.get("taskTitle")));
538                i18nKey = "PLUGINS_WORKSPACES_PROJECT_SERVICE_ACTIVITY_STREAM_EVENT_TASK_COMMENTED_DESC";
539                break;
540            case ObservationConstants.EVENT_TASK_CLOSED_STATUS_CHANGED :
541                parameters.put("task", new I18nizableText((String) activity.get("taskTitle")));
542                
543                boolean isClosed = "true".equals(activity.get("isClosed"));
544                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";
545                break;
546            default:
547                break;
548        }
549        
550        if (i18nKey != null)
551        {
552            I18nizableText descriptionI18n = new I18nizableText("plugin.workspaces", i18nKey, parameters);
553            description = _i18nUtils.translate(descriptionI18n, lang);
554        }
555        
556        return description;
557    }
558    
559    /**
560     * Aggregates a list of elements from the activity.
561     * activity = {
562     *      mapKey : [
563     *          {
564     *              titleKey : "Title 1",
565     *              …
566     *          },
567     *          {
568     *              titleKey : "Title 2",
569     *              …
570     *          },
571     *          {
572     *              titleKey : "Title 3",
573     *              …
574     *          }
575     *      ],
576     *      …
577     *  }
578     * @param activity the activity to use
579     * @param mapKey the key of the list to use
580     * @param titleKey title to use from the map
581     * @return a list, separated with commas, between each titles
582     */
583    protected String getTitles(Map<String, Object> activity, String mapKey, String titleKey)
584    {
585        @SuppressWarnings("unchecked")
586        List<Map<String, String>> items = (List<Map<String, String>>) activity.get(mapKey);
587        
588        
589        if (items == null)
590        {
591            return (String) activity.get(titleKey);
592        }
593        else
594        {
595            String result = null;
596            
597            for (Map<String, String> item : items)
598            {
599                if (result == null)
600                {
601                    result = item.get(titleKey);
602                }
603                else
604                {
605                    result += ", " + item.get(titleKey);
606                }
607            }
608            
609            return result;
610        }
611    }
612
613}