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