@yrpri/api 9.0.101 → 9.0.103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (638) hide show
  1. package/active-citizen/engine/allOurIdeas/explainAnswersAssistant.js +11 -1
  2. package/active-citizen/llms/baseChatBot.js +8 -75
  3. package/active-citizen/llms/imageGeneration/chatGptImageGenerator.js +56 -0
  4. package/active-citizen/llms/imageGeneration/collectionImageGenerator.js +6 -0
  5. package/agents/assistants/agentAssistant.js +3 -2
  6. package/agents/assistants/baseAssistant.js +7 -30
  7. package/agents/assistants/baseAssistantWithVoice.js +3 -3
  8. package/agents/assistants/voiceAssistant.js +2 -2
  9. package/agents/controllers/agentProductController.js +2 -1
  10. package/agents/controllers/agentSubscriptionController.js +20 -11
  11. package/agents/controllers/assistantsController.js +171 -127
  12. package/agents/controllers/policySynthAgents.js +33 -12
  13. package/agents/managers/newAiModelSetup.js +96 -0
  14. package/agents/managers/subscriptionManager.js +2 -2
  15. package/app.js +12 -12
  16. package/authorization.cjs +4 -0
  17. package/controllers/allOurIdeas.js +2 -1
  18. package/controllers/communities.cjs +47 -1
  19. package/controllers/groups.cjs +67 -4
  20. package/controllers/index.cjs +7 -7
  21. package/controllers/nonSpa.cjs +1 -1
  22. package/dist/active-citizen/controllers/activities.cjs +174 -0
  23. package/dist/active-citizen/controllers/news_feeds.cjs +96 -0
  24. package/dist/active-citizen/controllers/notifications.cjs +243 -0
  25. package/dist/active-citizen/controllers/recommendations.cjs +280 -0
  26. package/dist/active-citizen/engine/allOurIdeas/aiHelper.js +204 -0
  27. package/dist/active-citizen/engine/allOurIdeas/explainAnswersAssistant.js +66 -0
  28. package/dist/active-citizen/engine/allOurIdeas/iconGenerator.js +38 -0
  29. package/dist/active-citizen/engine/analytics/export_anon_community_activities.js +334 -0
  30. package/dist/active-citizen/engine/analytics/importer.js +308 -0
  31. package/dist/active-citizen/engine/analytics/manager.cjs +377 -0
  32. package/dist/active-citizen/engine/analytics/plausible/manager.cjs +500 -0
  33. package/dist/active-citizen/engine/analytics/statsCalc.cjs +194 -0
  34. package/dist/active-citizen/engine/analytics/utils.cjs +369 -0
  35. package/dist/active-citizen/engine/marketing/campaign.cjs +90 -0
  36. package/dist/active-citizen/engine/moderation/fraud/CreateFraudAuditReport.cjs +313 -0
  37. package/dist/active-citizen/engine/moderation/fraud/FraudBase.cjs +239 -0
  38. package/dist/active-citizen/engine/moderation/fraud/FraudDeleteBase.cjs +211 -0
  39. package/dist/active-citizen/engine/moderation/fraud/FraudDeleteEndorsements.cjs +120 -0
  40. package/dist/active-citizen/engine/moderation/fraud/FraudDeletePointQualities.cjs +114 -0
  41. package/dist/active-citizen/engine/moderation/fraud/FraudDeletePoints.cjs +101 -0
  42. package/dist/active-citizen/engine/moderation/fraud/FraudDeletePosts.cjs +84 -0
  43. package/dist/active-citizen/engine/moderation/fraud/FraudDeleteRatings.cjs +15 -0
  44. package/dist/active-citizen/engine/moderation/fraud/FraudGetBase.cjs +133 -0
  45. package/dist/active-citizen/engine/moderation/fraud/FraudGetEndorsements.cjs +185 -0
  46. package/dist/active-citizen/engine/moderation/fraud/FraudGetPointQualities.cjs +184 -0
  47. package/dist/active-citizen/engine/moderation/fraud/FraudGetPoints.cjs +170 -0
  48. package/dist/active-citizen/engine/moderation/fraud/FraudGetPosts.cjs +109 -0
  49. package/dist/active-citizen/engine/moderation/fraud/FraudGetRatings.cjs +140 -0
  50. package/dist/active-citizen/engine/moderation/fraud/FraudScannerNotifier.cjs +279 -0
  51. package/dist/active-citizen/engine/moderation/get_moderation_items.cjs +386 -0
  52. package/dist/active-citizen/engine/moderation/image_labeling/CommunityLabeling.cjs +49 -0
  53. package/dist/active-citizen/engine/moderation/image_labeling/GroupLabeling.cjs +68 -0
  54. package/dist/active-citizen/engine/moderation/image_labeling/ImageLabelingBase.cjs +288 -0
  55. package/dist/active-citizen/engine/moderation/image_labeling/PointLabeling.cjs +33 -0
  56. package/dist/active-citizen/engine/moderation/image_labeling/PostLabeling.cjs +56 -0
  57. package/dist/active-citizen/engine/moderation/perspective_api_client.cjs +106 -0
  58. package/dist/active-citizen/engine/moderation/process_moderation_items.cjs +344 -0
  59. package/dist/active-citizen/engine/moderation/toxicity_analysis.cjs +810 -0
  60. package/dist/active-citizen/engine/news_feeds/activity_and_item_index_definitions.cjs +15 -0
  61. package/dist/active-citizen/engine/news_feeds/generate_dynamically.cjs +362 -0
  62. package/dist/active-citizen/engine/news_feeds/generate_from_notifications.cjs +268 -0
  63. package/dist/active-citizen/engine/news_feeds/news_feeds_utils.cjs +439 -0
  64. package/dist/active-citizen/engine/notifications/emails_utils.cjs +569 -0
  65. package/dist/active-citizen/engine/notifications/generate_point_notifications.cjs +233 -0
  66. package/dist/active-citizen/engine/notifications/generate_post_notifications.cjs +118 -0
  67. package/dist/active-citizen/engine/notifications/generate_post_status_change_notifications.cjs +41 -0
  68. package/dist/active-citizen/engine/notifications/notifications_utils.cjs +148 -0
  69. package/dist/active-citizen/engine/notifications/point_delivery.cjs +54 -0
  70. package/dist/active-citizen/engine/notifications/post_delivery.cjs +31 -0
  71. package/dist/active-citizen/engine/notifications/process_delayed_notifications.cjs +471 -0
  72. package/dist/active-citizen/engine/notifications/process_general_notifications.cjs +212 -0
  73. package/dist/active-citizen/engine/old/exporters/categories_dataset.js +153 -0
  74. package/dist/active-citizen/engine/old/exporters/dataset_tools.js +80 -0
  75. package/dist/active-citizen/engine/old/exporters/sentiment_dataset.js +157 -0
  76. package/dist/active-citizen/engine/recommendations/events_importer.cjs +139 -0
  77. package/dist/active-citizen/engine/recommendations/events_manager.cjs +212 -0
  78. package/dist/active-citizen/engine/reports/add_points_to_sheet.cjs +83 -0
  79. package/dist/active-citizen/engine/reports/commonUtils.js +75 -0
  80. package/dist/active-citizen/engine/reports/common_utils.cjs +740 -0
  81. package/dist/active-citizen/engine/reports/docx_group_report.cjs +596 -0
  82. package/dist/active-citizen/engine/reports/xlsAllOurIdeasExport.js +232 -0
  83. package/dist/active-citizen/engine/reports/xls_community_users_report.cjs +277 -0
  84. package/dist/active-citizen/engine/reports/xls_group_report.cjs +718 -0
  85. package/dist/active-citizen/llms/baseChatBot.js +183 -0
  86. package/dist/active-citizen/llms/imageGeneration/chatGptImageGenerator.js +56 -0
  87. package/dist/active-citizen/llms/imageGeneration/collectionImageGenerator.js +109 -0
  88. package/dist/active-citizen/llms/imageGeneration/dalleImageGenerator.js +84 -0
  89. package/dist/active-citizen/llms/imageGeneration/fluxImageGenerator.js +49 -0
  90. package/dist/active-citizen/llms/imageGeneration/imageProcessorService.js +64 -0
  91. package/dist/active-citizen/llms/imageGeneration/imagenImageGenerator.js +107 -0
  92. package/dist/active-citizen/llms/imageGeneration/s3Service.js +110 -0
  93. package/dist/active-citizen/llms/llmTranslation.js +472 -0
  94. package/dist/active-citizen/models/ac_activity.cjs +216 -0
  95. package/dist/active-citizen/models/ac_background_job.cjs +109 -0
  96. package/dist/active-citizen/models/ac_campaign.cjs +97 -0
  97. package/dist/active-citizen/models/ac_client_activity.cjs +23 -0
  98. package/dist/active-citizen/models/ac_delayed_notification.cjs +43 -0
  99. package/dist/active-citizen/models/ac_following.cjs +43 -0
  100. package/dist/active-citizen/models/ac_list.cjs +68 -0
  101. package/dist/active-citizen/models/ac_list_users.cjs +19 -0
  102. package/dist/active-citizen/models/ac_mute.cjs +27 -0
  103. package/dist/active-citizen/models/ac_news_feed_item.cjs +57 -0
  104. package/dist/active-citizen/models/ac_news_feed_processed_range.cjs +59 -0
  105. package/dist/active-citizen/models/ac_notification.cjs +292 -0
  106. package/dist/active-citizen/models/ac_translation_cache.cjs +750 -0
  107. package/dist/active-citizen/models/ac_watching.cjs +31 -0
  108. package/dist/active-citizen/scripts/analytics/setup_all_plausible_goals.cjs +13 -0
  109. package/dist/active-citizen/scripts/fix_old_delayed_notifications.js +63 -0
  110. package/dist/active-citizen/scripts/kue_status.js +31 -0
  111. package/dist/active-citizen/scripts/kue_watch_stuck_jobs.js +24 -0
  112. package/dist/active-citizen/scripts/translation_clear_language.js +117 -0
  113. package/dist/active-citizen/scripts/translation_delete.js +27 -0
  114. package/dist/active-citizen/scripts/translation_replace_text_from_url.js +180 -0
  115. package/dist/active-citizen/scripts/translation_update.js +28 -0
  116. package/dist/active-citizen/scripts/translations_list.js +27 -0
  117. package/{active-citizen/utils/airbrake.js → dist/active-citizen/utils/airbrake.cjs} +1 -1
  118. package/dist/active-citizen/utils/get_anonymous_system_user.cjs +21 -0
  119. package/dist/active-citizen/utils/i18n.cjs +3 -0
  120. package/dist/active-citizen/utils/logger.cjs +25 -0
  121. package/dist/active-citizen/utils/redisConnection.cjs +29 -0
  122. package/dist/active-citizen/utils/to_json.cjs +9 -0
  123. package/dist/active-citizen/utils/translation_cloning.cjs +171 -0
  124. package/dist/active-citizen/utils/translation_helpers.cjs +534 -0
  125. package/dist/active-citizen/utils/truncate_text.cjs +21 -0
  126. package/dist/active-citizen/utils/updateAllLocalesFromEn.js +253 -0
  127. package/dist/active-citizen/utils/updateLocaleFolders.js +34 -0
  128. package/dist/active-citizen/workers/activity.cjs +189 -0
  129. package/dist/active-citizen/workers/anonymizations.cjs +734 -0
  130. package/dist/active-citizen/workers/bulk_status_update.cjs +458 -0
  131. package/dist/active-citizen/workers/delayed_jobs.cjs +244 -0
  132. package/dist/active-citizen/workers/deletions.cjs +1911 -0
  133. package/dist/active-citizen/workers/email.cjs +9 -0
  134. package/dist/active-citizen/workers/fraud_management.cjs +109 -0
  135. package/dist/active-citizen/workers/generativeAi.js +56 -0
  136. package/dist/active-citizen/workers/main.cjs +89 -0
  137. package/dist/active-citizen/workers/marketing.cjs +25 -0
  138. package/dist/active-citizen/workers/moderation.cjs +73 -0
  139. package/dist/active-citizen/workers/notification_delivery.cjs +368 -0
  140. package/dist/active-citizen/workers/notification_news_feed.cjs +142 -0
  141. package/dist/active-citizen/workers/queue.cjs +99 -0
  142. package/dist/active-citizen/workers/recount.cjs +74 -0
  143. package/dist/active-citizen/workers/reports.cjs +42 -0
  144. package/dist/active-citizen/workers/similarities.cjs +21 -0
  145. package/dist/active-citizen/workers/speech_to_text.cjs +482 -0
  146. package/dist/agents/assistants/agentAssistant.js +88 -0
  147. package/dist/agents/assistants/baseAssistant.js +888 -0
  148. package/dist/agents/assistants/baseAssistantWithVoice.js +150 -0
  149. package/dist/agents/assistants/modes/agentDirectConnection.js +84 -0
  150. package/dist/agents/assistants/modes/agentSelectionMode.js +44 -0
  151. package/dist/agents/assistants/modes/baseAssistantMode.js +54 -0
  152. package/dist/agents/assistants/modes/tools/agentTools.js +447 -0
  153. package/dist/agents/assistants/modes/tools/baseTools.js +58 -0
  154. package/dist/agents/assistants/modes/tools/loginTools.js +156 -0
  155. package/dist/agents/assistants/modes/tools/models/agents.js +146 -0
  156. package/dist/agents/assistants/modes/tools/models/subscriptions.js +332 -0
  157. package/dist/agents/assistants/modes/tools/models/users.js +11 -0
  158. package/dist/agents/assistants/modes/tools/navigationTools.js +166 -0
  159. package/{agents/assistants/modes/tools/workflowConversationTools.js → dist/agents/assistants/modes/tools/subscriptionTools.js} +1 -4
  160. package/{agents/assistants/modes/tools/workflowTools.js → dist/agents/assistants/modes/tools/workflowConverstationTools.js} +1 -1
  161. package/dist/agents/assistants/voiceAssistant.js +619 -0
  162. package/dist/agents/controllers/agentProductController.js +103 -0
  163. package/dist/agents/controllers/agentSubscriptionController.js +261 -0
  164. package/dist/agents/controllers/assistantsController.js +511 -0
  165. package/dist/agents/controllers/policySynthAgents.js +395 -0
  166. package/{agents → dist/agents/managers}/agentProductManager.js +2 -2
  167. package/dist/agents/managers/emailInvitesManager.js +55 -0
  168. package/dist/agents/managers/emailTemplateRenderer.js +362 -0
  169. package/dist/agents/managers/newAiModelSetup.js +650 -0
  170. package/dist/agents/managers/notificationAgentQueueManager.js +510 -0
  171. package/dist/agents/managers/subscriptionManager.js +535 -0
  172. package/dist/agents/managers/workflowConversationManager.js +79 -0
  173. package/dist/agents/models/agentProduct.js +116 -0
  174. package/dist/agents/models/agentProductBoosterPurchase.js +58 -0
  175. package/dist/agents/models/agentProductBundle.js +68 -0
  176. package/dist/agents/models/agentProductRun.js +52 -0
  177. package/dist/agents/models/discount.js +88 -0
  178. package/dist/agents/models/subscription.js +79 -0
  179. package/dist/agents/models/subscriptionPlan.js +46 -0
  180. package/dist/agents/models/subscriptionUser.js +27 -0
  181. package/dist/agents/models/testData/createEvolyAgentProduct.js +477 -0
  182. package/dist/agents/models/testData/old/updateAgentWorkflowConfiguration.js +230 -0
  183. package/dist/agents/models/testData/setupEvolyAgentProductConfig.js +233 -0
  184. package/dist/agents/models/testData/updateAgentWorkflowConfiguration.js +230 -0
  185. package/{agents/models/workflowConverstation.js → dist/agents/models/workflowConversation.js} +1 -1
  186. package/{agents/tools/updateTemplateWorkflow.js → dist/agents/tools/setTemplateWorkflowCommunityId.js} +1 -0
  187. package/dist/app.js +943 -0
  188. package/dist/authorization.cjs +1860 -0
  189. package/dist/bot_control.js +1930 -0
  190. package/dist/config/config.cjs +14 -0
  191. package/dist/config/config.js +14 -0
  192. package/dist/controllers/allOurIdeas.js +696 -0
  193. package/dist/controllers/audios.cjs +100 -0
  194. package/dist/controllers/bulkStatusUpdates.cjs +202 -0
  195. package/dist/controllers/categories.cjs +199 -0
  196. package/dist/controllers/communities.cjs +2996 -0
  197. package/dist/controllers/domains.cjs +1341 -0
  198. package/dist/controllers/externalIds.cjs +223 -0
  199. package/dist/controllers/groups.cjs +4309 -0
  200. package/dist/controllers/images.cjs +499 -0
  201. package/dist/controllers/index.cjs +449 -0
  202. package/dist/controllers/legacyPages.cjs +35 -0
  203. package/dist/controllers/legacyPosts.cjs +56 -0
  204. package/dist/controllers/legacyUsers.cjs +36 -0
  205. package/dist/controllers/nonSpa.cjs +574 -0
  206. package/dist/controllers/organizations.cjs +250 -0
  207. package/dist/controllers/points.cjs +1137 -0
  208. package/dist/controllers/posts.cjs +2036 -0
  209. package/dist/controllers/ratings.cjs +234 -0
  210. package/dist/controllers/users.cjs +2255 -0
  211. package/dist/controllers/videos.cjs +226 -0
  212. package/dist/deleteUnwantedDeclerations.cjs +55 -0
  213. package/dist/migrations/agentAuditLogs.cjs +46 -0
  214. package/dist/migrations/agentClasses.cjs +60 -0
  215. package/dist/migrations/agentConnectorClasses.cjs +61 -0
  216. package/dist/migrations/agentConnectors.cjs +50 -0
  217. package/dist/migrations/agentEvals.cjs +45 -0
  218. package/dist/migrations/agentRegistries.cjs +40 -0
  219. package/dist/migrations/agents.cjs +54 -0
  220. package/dist/migrations/aiModels.cjs +49 -0
  221. package/dist/migrations/apiUsage.cjs +47 -0
  222. package/dist/migrations/apis.cjs +49 -0
  223. package/dist/migrations/groupPrivateData.cjs +30 -0
  224. package/dist/migrations/modelUsage.cjs +60 -0
  225. package/dist/migrations/oldMigrations/2019/20181030020612-AddActivitiesIndex.js +23 -0
  226. package/dist/migrations/oldMigrations/2019/20181102210612-AddFirstVideoFeatures.js +360 -0
  227. package/dist/migrations/oldMigrations/2019/20181212210612-ModerationFeatures.js +29 -0
  228. package/dist/migrations/oldMigrations/2019/2019010610612-CommunityFolders.js +43 -0
  229. package/dist/migrations/oldMigrations/2019/20190117020612-AddMissingIndexes.js +24 -0
  230. package/dist/migrations/oldMigrations/2019/20190117020612-RemoveUnusedIndexes.js +29 -0
  231. package/dist/migrations/oldMigrations/2019/20190127020612-RemoveUnusedIndexesPartThree.js +22 -0
  232. package/dist/migrations/oldMigrations/2019/20190127020612-RemoveUnusedIndexesPartTwo.js +23 -0
  233. package/{migrations/200824_create_embeddings_and_org_update.cjs → dist/migrations/oldMigrations/2019/20190223020612-AddPrivateProfileDataToUsers.js} +6 -3
  234. package/dist/migrations/oldMigrations/2019/20190706210612-AddCustomRatings.js +43 -0
  235. package/dist/migrations/oldMigrations/2019/20190829210612-AddGeneralStore.js +36 -0
  236. package/dist/migrations/oldMigrations/2019/20192811210612-AddAcClientActivities.js +41 -0
  237. package/dist/migrations/oldMigrations/2020/20190527020612-WorkOnIndexes.js +88 -0
  238. package/dist/migrations/oldMigrations/2020/20200409020612-AddBackgroundJob.js +33 -0
  239. package/dist/migrations/oldMigrations/2020/20200716210612-AddDataToCollections.js +38 -0
  240. package/dist/migrations/oldMigrations/2022/20220215100612-AddDataToEndorsements.js +19 -0
  241. package/dist/migrations/oldMigrations/2022/20220220100612-AddDataForFraudDetection.js +19 -0
  242. package/dist/migrations/oldMigrations/2022/20220903100612-AddPromotionFeatures.js +127 -0
  243. package/dist/migrations/oldMigrations/2022/onHold/20200527020612-AddCampaigns.js +68 -0
  244. package/dist/migrations/oldMigrations/2024/20241304175112-AddMediaSupportForHtmlGroups.cjs +63 -0
  245. package/dist/migrations/oldMigrations/older/20160511172514-AddNotificationFeatures.js +14 -0
  246. package/dist/migrations/oldMigrations/older/20161030020612-AddBulkStatusUpdate.js +71 -0
  247. package/dist/migrations/oldMigrations/older/20170514035258-add-metadata-to-invites.js +12 -0
  248. package/dist/migrations/oldMigrations/older/20180216020612-AddTranslationCaches.js +46 -0
  249. package/dist/migrations/oldMigrations/older/20180218210612-AddTranslationAndLanguages.js +46 -0
  250. package/dist/migrations/privateAccessStore.cjs +55 -0
  251. package/dist/migrations/zzz_associations.cjs +154 -0
  252. package/dist/migrations/zzzzz_create_agent_runs.cjs +606 -0
  253. package/dist/migrations/zzzzzz_create_agent_runs_fix.cjs +11 -0
  254. package/dist/migrations/zzzzzzz_create_trees.cjs +81 -0
  255. package/dist/models/audio.cjs +430 -0
  256. package/dist/models/bulk_status_update.cjs +58 -0
  257. package/dist/models/campaign.cjs +78 -0
  258. package/dist/models/category.cjs +94 -0
  259. package/dist/models/community.cjs +337 -0
  260. package/dist/models/domain.cjs +486 -0
  261. package/dist/models/endorsement.cjs +39 -0
  262. package/dist/models/general_data_store.cjs +20 -0
  263. package/dist/models/group.cjs +728 -0
  264. package/dist/models/image.cjs +579 -0
  265. package/dist/models/index.cjs +186 -0
  266. package/dist/models/invite.cjs +48 -0
  267. package/dist/models/iso_country.cjs +16 -0
  268. package/dist/models/organization.cjs +122 -0
  269. package/dist/models/page.cjs +273 -0
  270. package/dist/models/point.cjs +622 -0
  271. package/dist/models/point_quality.cjs +39 -0
  272. package/dist/models/point_revision.cjs +47 -0
  273. package/dist/models/post.cjs +680 -0
  274. package/dist/models/post_revision.cjs +38 -0
  275. package/dist/models/post_status_change.cjs +35 -0
  276. package/dist/models/promotion.cjs +34 -0
  277. package/dist/models/rating.cjs +51 -0
  278. package/dist/models/relationship.cjs +19 -0
  279. package/dist/models/request_to_join.cjs +20 -0
  280. package/dist/models/user.cjs +604 -0
  281. package/dist/models/user_legacy_password.cjs +13 -0
  282. package/dist/models/video.cjs +1137 -0
  283. package/dist/publish.js +40 -0
  284. package/dist/repack.js +53 -0
  285. package/dist/scripts/addRatingUsersToGroup.js +51 -0
  286. package/dist/scripts/addUserToOrganization.js +71 -0
  287. package/dist/scripts/analyseRatingsForCommunity.js +150 -0
  288. package/dist/scripts/analyzeAndFixBrokenPointUsers.js +28 -0
  289. package/dist/scripts/analyzeEndorsementsByCountry.js +70 -0
  290. package/dist/scripts/analyzePostsForCommunity.js +185 -0
  291. package/dist/scripts/bulkStatusUpdates/listUpdates.js +14 -0
  292. package/dist/scripts/bulkStatusUpdates/mergeLatestPostsToUpdate.js +110 -0
  293. package/dist/scripts/bulkStatusUpdates/performUpdate.js +116 -0
  294. package/{scripts/bulkStatusUpdates/performUpdateForGroup.js → dist/scripts/bulkStatusUpdates/performUpdateForGroup.cjs} +1 -2
  295. package/dist/scripts/bulkStatusUpdates/performUpdateForStatus.js +141 -0
  296. package/dist/scripts/change/changeVideoAspectTo.js +34 -0
  297. package/dist/scripts/change/setUseNewVersion.cjs +22 -0
  298. package/dist/scripts/changeCommunityGroupcount.js +30 -0
  299. package/dist/scripts/changeCommunityPostCount.js +30 -0
  300. package/dist/scripts/changeGroupPostCount.js +30 -0
  301. package/dist/scripts/changeLanguage.js +50 -0
  302. package/dist/scripts/changeOfficalStatus.js +30 -0
  303. package/{scripts/cleanups/deleteAnonNotifications.js → dist/scripts/cleanups/deleteAnonNotifications.cjs} +1 -1
  304. package/dist/scripts/cleanups/deleteLinkGroupsWithDeletedCommunities.js +62 -0
  305. package/dist/scripts/cleanups/deleteYearOldNotifications.cjs +72 -0
  306. package/dist/scripts/cleanups/removeAllUsersFromHiddenPublicGroup.js +43 -0
  307. package/dist/scripts/clearAllEndorsementInGroup.js +50 -0
  308. package/dist/scripts/cloning/clearUsersForCommunitiesFromUrl.js +129 -0
  309. package/dist/scripts/cloning/cloneFromUrlScript.js +65 -0
  310. package/dist/scripts/cloning/cloneWBFromUrlScriptAndCreateLinks.js +140 -0
  311. package/dist/scripts/cloning/cloneWBFromUrlScriptNoUsersOrPoints.js +140 -0
  312. package/dist/scripts/cloning/cloneWBSerbianFromUrlScriptAndCreateLinks.js +131 -0
  313. package/dist/scripts/cloning/copyCommunityConfigAndTranslationsFromURL.js +173 -0
  314. package/dist/scripts/cloning/copyCommunityOneGroupToDomainNoUsersNoEndorsements.js +18 -0
  315. package/dist/scripts/cloning/copyCommunityToDomainNoUsersNoEndorsements.js +17 -0
  316. package/dist/scripts/cloning/copyCommunityToDomainWithEverything.js +17 -0
  317. package/dist/scripts/cloning/copyCommunityToDomainWithOnlyGroups.js +26 -0
  318. package/dist/scripts/cloning/copyGroupConfigAndTranslationsFromURL.js +205 -0
  319. package/dist/scripts/cloning/copyPostToGroupOld.js +397 -0
  320. package/dist/scripts/cloning/copyPostVideosFromURL.js +236 -0
  321. package/dist/scripts/cloning/copyPostWithOutAnyVotingOrActivities.js +17 -0
  322. package/dist/scripts/cloning/deepCloneSerbianWBFromUrlScriptAndCreateLinks.js +131 -0
  323. package/dist/scripts/cloning/deepCloneWBFromUrlScriptAndCreateLinks.js +139 -0
  324. package/dist/scripts/cloning/setAdminsFromURL.js +161 -0
  325. package/dist/scripts/cloning/setExternalIdsFromURL.js +129 -0
  326. package/dist/scripts/countCommunity.js +291 -0
  327. package/dist/scripts/countCommunityUsers.js +152 -0
  328. package/dist/scripts/countDelayedNotifications.js +18 -0
  329. package/dist/scripts/countGroup.js +246 -0
  330. package/dist/scripts/countStuff.js +67 -0
  331. package/dist/scripts/countUniqueVotersInAGroup.js +48 -0
  332. package/dist/scripts/createInvitesAndShow.js +75 -0
  333. package/dist/scripts/database/sync_database.js +14 -0
  334. package/dist/scripts/database/sync_dev_database.js +17 -0
  335. package/dist/scripts/debugNotifications.js +58 -0
  336. package/dist/scripts/deleteAllNewsFeeds.js +10 -0
  337. package/dist/scripts/deleteCategory.js +13 -0
  338. package/dist/scripts/deleteOldAppActivities.js +40 -0
  339. package/dist/scripts/deletePostContactDataForCommunity.js +53 -0
  340. package/dist/scripts/destroy/destroy_all_but_one_domain.js +1026 -0
  341. package/dist/scripts/displayAuthorForPost.js +16 -0
  342. package/dist/scripts/endorsementFraudDetection/analyseEndorsementsForCommunity.js +183 -0
  343. package/dist/scripts/endorsementFraudDetection/bulkDeleteDuplicateEndorsmentsFromUrl.js +208 -0
  344. package/dist/scripts/exportAllStatusChanges.js +36 -0
  345. package/dist/scripts/exportClientAcitivity.js +36 -0
  346. package/dist/scripts/exportEndorsementsForCommunity.js +79 -0
  347. package/dist/scripts/exportPointQualitiesForCommunity.js +84 -0
  348. package/dist/scripts/exportPostsAndPointsForCommunity.js +147 -0
  349. package/dist/scripts/exportPostsDataSetForDomain.js +244 -0
  350. package/dist/scripts/exportPostsForGroup.js +173 -0
  351. package/dist/scripts/exportRatingsForPost.js +15 -0
  352. package/dist/scripts/exportUserEndorsementsWithUserAnalysis.js +123 -0
  353. package/dist/scripts/exportUsersForCommunity.js +24 -0
  354. package/dist/scripts/exportUsersForDomain.js +24 -0
  355. package/dist/scripts/exportUsersForGroup.js +24 -0
  356. package/dist/scripts/exports/ratingDistribution.js +71 -0
  357. package/dist/scripts/exports/whoEndorsedWhatByCommunity.js +56 -0
  358. package/dist/scripts/findUnusedClientImports.js +56 -0
  359. package/dist/scripts/fixAllPostPointCounts.js +22 -0
  360. package/dist/scripts/fixAnonNotificationsSettings.js +48 -0
  361. package/dist/scripts/fixCountKopavogur.js +9 -0
  362. package/dist/scripts/fixEndorsementsAfterCopyPostToGroup.js +190 -0
  363. package/dist/scripts/fixExternalUserId.js +24 -0
  364. package/dist/scripts/fixGroupAccess.js +16 -0
  365. package/dist/scripts/fixGroupIdeasAndPointsCount.js +49 -0
  366. package/dist/scripts/fixNotificationSettings.js +39 -0
  367. package/dist/scripts/fixSurveyRadioBakedInSubCodes.js +64 -0
  368. package/dist/scripts/fixWrongUserIdForStatusUpdates.js +49 -0
  369. package/dist/scripts/gallery/exportGalleryData.js +40 -0
  370. package/dist/scripts/gallery/importGalleryForCommunity.js +168 -0
  371. package/dist/scripts/gallery/readJsonAndDownloadImagesVersion2.js +55 -0
  372. package/dist/scripts/gallery/refreshAcApiPostIdsForCommunity.js +58 -0
  373. package/dist/scripts/genderAnalysis.js +63 -0
  374. package/dist/scripts/genderAnalysisByStatus.js +62 -0
  375. package/dist/scripts/importAllLocalesFromLocalFolders.js +55 -0
  376. package/dist/scripts/importDomain.js +1652 -0
  377. package/dist/scripts/keys/addOidcToDomain.cjs +61 -0
  378. package/dist/scripts/landUseGame/export3Ddata.js +162 -0
  379. package/dist/scripts/listLanguagesForGroup.js +54 -0
  380. package/dist/scripts/loadTestCreateDummyContentForGroup.js +27 -0
  381. package/dist/scripts/makeRecursiveMapData.js +103 -0
  382. package/dist/scripts/mapping/community_map_csv.js +145 -0
  383. package/dist/scripts/moveCommunityToDomain.js +22 -0
  384. package/dist/scripts/moveGroupToCommunity.js +23 -0
  385. package/dist/scripts/movePostToGroup.js +101 -0
  386. package/dist/scripts/movePostsToGroupsRecountGroupFromUrl.js +297 -0
  387. package/dist/scripts/oldMovePostToGroup.js +153 -0
  388. package/dist/scripts/processCsvForPdfUrls.js +37 -0
  389. package/dist/scripts/processCsvForTranslationAndToxicity.js +125 -0
  390. package/dist/scripts/recount/recount_recursive_communities.js +125 -0
  391. package/dist/scripts/recountALLCommunityGroupCounts.js +37 -0
  392. package/dist/scripts/recountAll.js +97 -0
  393. package/dist/scripts/recountCommunitesFromUrl.js +58 -0
  394. package/dist/scripts/recountCommunity.js +19 -0
  395. package/dist/scripts/recountGroup.js +218 -0
  396. package/dist/scripts/recountGroupNoUserChange.js +219 -0
  397. package/dist/scripts/resetAllEndorsementsForGroup.js +57 -0
  398. package/dist/scripts/resetEnTranslationForGroup.js +45 -0
  399. package/dist/scripts/setAdminOnAll.cjs +107 -0
  400. package/{scripts/change → dist/scripts}/setDomainAdmin.cjs +0 -2
  401. package/dist/scripts/setDomainLocales.js +33 -0
  402. package/dist/scripts/setEarlQuestionIdOnGroup.cjs +29 -0
  403. package/dist/scripts/setLanguageOnGroupCommunitesFromUrl.js +86 -0
  404. package/dist/scripts/setMemberOfAll.js +101 -0
  405. package/dist/scripts/setNewUserForContentOfCommunity.js +189 -0
  406. package/dist/scripts/setOfficialStatusOnAllPostsForCommunity.js +35 -0
  407. package/dist/scripts/setUserOnAll.js +101 -0
  408. package/dist/scripts/showCategoryForGroup.js +18 -0
  409. package/dist/scripts/showOldActivityTypes.js +14 -0
  410. package/dist/scripts/showPostsMissingCategoryForGroup.js +17 -0
  411. package/dist/scripts/showStatuses.js +17 -0
  412. package/dist/scripts/showUniqueVotersInCommunity.js +61 -0
  413. package/dist/scripts/showUserAgentsAndIpsForEmails.js +66 -0
  414. package/dist/scripts/simpleExportForGroupsForCommunity.js +46 -0
  415. package/dist/scripts/simpleExportForPointsForCommunity.js +82 -0
  416. package/dist/scripts/simpleExportForPostsForCommunity.js +61 -0
  417. package/dist/scripts/testForEndorsments.js +21 -0
  418. package/dist/scripts/undeleteGroupAndAllContent.js +151 -0
  419. package/dist/scripts/undeletePost.js +135 -0
  420. package/dist/scripts/unlinkSsn.js +23 -0
  421. package/dist/scripts/updateFromAlthingi.js +303 -0
  422. package/dist/server.js +3 -0
  423. package/dist/utils/airbrake.cjs +17 -0
  424. package/dist/utils/cjsCodeReview.js +99 -0
  425. package/dist/utils/community_mapping_tools.cjs +124 -0
  426. package/dist/utils/copy_utils.cjs +1399 -0
  427. package/dist/utils/docx_utils.cjs +464 -0
  428. package/dist/utils/export_utils.cjs +491 -0
  429. package/dist/utils/i18n.cjs +17 -0
  430. package/dist/utils/is_valid_db_id.cjs +28 -0
  431. package/dist/utils/logger.cjs +25 -0
  432. package/dist/utils/loggerTs.js +26 -0
  433. package/dist/utils/manifest_generator.cjs +104 -0
  434. package/dist/utils/parse_domain.cjs +16 -0
  435. package/dist/utils/recount_utils.cjs +415 -0
  436. package/dist/utils/sharing_parameters.cjs +111 -0
  437. package/dist/utils/sitemap_generator.cjs +286 -0
  438. package/dist/utils/to_json.cjs +14 -0
  439. package/dist/utils/ypLanguages.js +747 -0
  440. package/dist/webSockets.js +77 -0
  441. package/package.json +46 -44
  442. package/server.d.ts +104 -2
  443. package/utils/copy_utils.cjs +7 -1
  444. package/active-citizen/engine/allOurIdeas/aiHelper.d.ts +0 -21
  445. package/active-citizen/engine/allOurIdeas/aiHelper.d.ts.map +0 -1
  446. package/active-citizen/engine/allOurIdeas/explainAnswersAssistant.d.ts.map +0 -1
  447. package/active-citizen/engine/allOurIdeas/iconGenerator.d.ts +0 -12
  448. package/active-citizen/engine/allOurIdeas/iconGenerator.d.ts.map +0 -1
  449. package/active-citizen/engine/analytics/export_anon_community_activities.d.ts.map +0 -1
  450. package/active-citizen/engine/analytics/importer.d.ts.map +0 -1
  451. package/active-citizen/engine/old/exporters/categories_dataset.d.ts.map +0 -1
  452. package/active-citizen/engine/old/exporters/dataset_tools.d.ts.map +0 -1
  453. package/active-citizen/engine/old/exporters/sentiment_dataset.d.ts.map +0 -1
  454. package/active-citizen/engine/reports/xls_all_our_ideas_export.js +0 -141
  455. package/active-citizen/llms/baseChatBot.d.ts +0 -51
  456. package/active-citizen/llms/baseChatBot.d.ts.map +0 -1
  457. package/active-citizen/llms/collectionImageGenerator.js +0 -357
  458. package/active-citizen/llms/llmTranslation.d.ts +0 -24
  459. package/active-citizen/llms/llmTranslation.d.ts.map +0 -1
  460. package/active-citizen/scripts/fix_old_delayed_notifications.d.ts.map +0 -1
  461. package/active-citizen/scripts/kue_status.d.ts.map +0 -1
  462. package/active-citizen/scripts/kue_watch_stuck_jobs.d.ts.map +0 -1
  463. package/active-citizen/scripts/translation_clear_language.d.ts.map +0 -1
  464. package/active-citizen/scripts/translation_delete.d.ts.map +0 -1
  465. package/active-citizen/scripts/translation_replace_text_from_url.d.ts.map +0 -1
  466. package/active-citizen/scripts/translation_update.d.ts.map +0 -1
  467. package/active-citizen/scripts/translations_list.d.ts.map +0 -1
  468. package/active-citizen/utils/airbrake.d.ts.map +0 -1
  469. package/active-citizen/utils/updateAllLocalesFromEn.d.ts +0 -21
  470. package/active-citizen/utils/updateAllLocalesFromEn.d.ts.map +0 -1
  471. package/active-citizen/utils/updateLocaleFolders.d.ts +0 -2
  472. package/active-citizen/utils/updateLocaleFolders.d.ts.map +0 -1
  473. package/active-citizen/workers/cloudflare/voiceSession/worker.js +0 -213
  474. package/active-citizen/workers/generativeAi.d.ts.map +0 -1
  475. package/agents/assistants/agentAssistantOld.js +0 -863
  476. package/agents/assistants/modes/agentConfigurationMode.js +0 -63
  477. package/agents/assistants/modes/agentOperationsMode.js +0 -195
  478. package/agents/assistants/modes/commonTools.js +0 -58
  479. package/agents/assistants/modes/directConversationMode.js +0 -14
  480. package/agents/assistants/modes/toolHandlers/accountHandlers.js +0 -46
  481. package/agents/assistants/modes/toolHandlers/commonHandlers.js +0 -57
  482. package/agents/assistants/modes/toolHandlers/commonTools.js +0 -58
  483. package/agents/assistants/modes/toolHandlers/loginHandlers.js +0 -46
  484. package/agents/assistants/modes/toolHandlers/subscriptionHandlers.js +0 -469
  485. package/agents/assistants/modes/tools/agentConnectionTools.js +0 -326
  486. package/agents/assistants/modes/tools/commonHandlers.js +0 -57
  487. package/agents/assistants/modes/tools/loginHandlers.js +0 -46
  488. package/agents/assistants/modes/tools/subscriptionHandlers.js +0 -469
  489. package/agents/controllers/assistantController.js +0 -243
  490. package/agents/managers/workflowManager.js +0 -76
  491. package/agents/models/testData/setupConfig.js +0 -140
  492. package/agents/models/workflow.js +0 -53
  493. package/agents/subscriptionManager.js +0 -218
  494. package/app.d.ts +0 -40
  495. package/app.d.ts.map +0 -1
  496. package/bot_control.d.ts.map +0 -1
  497. package/publish.d.ts.map +0 -1
  498. package/repack.d.ts.map +0 -1
  499. package/scripts/addRatingUsersToGroup.d.ts.map +0 -1
  500. package/scripts/addUserToOrganization.d.ts.map +0 -1
  501. package/scripts/analyseRatingsForCommunity.d.ts.map +0 -1
  502. package/scripts/analyzeAndFixBrokenPointUsers.d.ts.map +0 -1
  503. package/scripts/analyzeEndorsementsByCountry.d.ts.map +0 -1
  504. package/scripts/analyzePostsForCommunity.d.ts.map +0 -1
  505. package/scripts/bulkStatusUpdates/listUpdates.d.ts.map +0 -1
  506. package/scripts/bulkStatusUpdates/mergeLatestPostsToUpdate.d.ts.map +0 -1
  507. package/scripts/bulkStatusUpdates/performUpdate.d.ts.map +0 -1
  508. package/scripts/bulkStatusUpdates/performUpdateForGroup.d.ts.map +0 -1
  509. package/scripts/bulkStatusUpdates/performUpdateForStatus.d.ts.map +0 -1
  510. package/scripts/change/changeVideoAspectTo.d.ts.map +0 -1
  511. package/scripts/changeCommunityGroupcount.d.ts.map +0 -1
  512. package/scripts/changeCommunityPostCount.d.ts.map +0 -1
  513. package/scripts/changeGroupPostCount.d.ts.map +0 -1
  514. package/scripts/changeLanguage.d.ts.map +0 -1
  515. package/scripts/changeOfficalStatus.d.ts.map +0 -1
  516. package/scripts/cleanups/deleteAnonNotifications.d.ts.map +0 -1
  517. package/scripts/cleanups/deleteLinkGroupsWithDeletedCommunities.d.ts.map +0 -1
  518. package/scripts/cleanups/removeAllUsersFromHiddenPublicGroup.d.ts.map +0 -1
  519. package/scripts/clearAllEndorsementInGroup.d.ts.map +0 -1
  520. package/scripts/cloning/clearUsersForCommunitiesFromUrl.d.ts.map +0 -1
  521. package/scripts/cloning/cloneFromUrlScript.d.ts.map +0 -1
  522. package/scripts/cloning/cloneWBFromUrlScriptAndCreateLinks.d.ts.map +0 -1
  523. package/scripts/cloning/cloneWBFromUrlScriptNoUsersOrPoints.d.ts.map +0 -1
  524. package/scripts/cloning/cloneWBSerbianFromUrlScriptAndCreateLinks.d.ts.map +0 -1
  525. package/scripts/cloning/copyCommunityConfigAndTranslationsFromURL.d.ts.map +0 -1
  526. package/scripts/cloning/copyCommunityOneGroupToDomainNoUsersNoEndorsements.d.ts.map +0 -1
  527. package/scripts/cloning/copyCommunityToDomainNoUsersNoEndorsements.d.ts.map +0 -1
  528. package/scripts/cloning/copyCommunityToDomainWithEverything.d.ts.map +0 -1
  529. package/scripts/cloning/copyCommunityToDomainWithOnlyGroups.d.ts.map +0 -1
  530. package/scripts/cloning/copyGroupConfigAndTranslationsFromURL.d.ts.map +0 -1
  531. package/scripts/cloning/copyPostToGroupOld.d.ts.map +0 -1
  532. package/scripts/cloning/copyPostVideosFromURL.d.ts.map +0 -1
  533. package/scripts/cloning/copyPostWithOutAnyVotingOrActivities.d.ts.map +0 -1
  534. package/scripts/cloning/deepCloneSerbianWBFromUrlScriptAndCreateLinks.d.ts.map +0 -1
  535. package/scripts/cloning/deepCloneWBFromUrlScriptAndCreateLinks.d.ts.map +0 -1
  536. package/scripts/cloning/setAdminsFromURL.d.ts.map +0 -1
  537. package/scripts/cloning/setExternalIdsFromURL.d.ts.map +0 -1
  538. package/scripts/countCommunity.d.ts.map +0 -1
  539. package/scripts/countCommunityUsers.d.ts.map +0 -1
  540. package/scripts/countDelayedNotifications.d.ts.map +0 -1
  541. package/scripts/countGroup.d.ts.map +0 -1
  542. package/scripts/countStuff.d.ts.map +0 -1
  543. package/scripts/countUniqueVotersInAGroup.d.ts.map +0 -1
  544. package/scripts/createInvitesAndShow.d.ts.map +0 -1
  545. package/scripts/database/seedTestAiModels.js +0 -64
  546. package/scripts/database/sync_database.d.ts.map +0 -1
  547. package/scripts/database/sync_dev_database.d.ts.map +0 -1
  548. package/scripts/debugNotifications.d.ts.map +0 -1
  549. package/scripts/deleteAllNewsFeeds.d.ts.map +0 -1
  550. package/scripts/deleteCategory.d.ts.map +0 -1
  551. package/scripts/deleteOldAppActivities.d.ts.map +0 -1
  552. package/scripts/deletePostContactDataForCommunity.d.ts.map +0 -1
  553. package/scripts/destroy/destroy_all_but_one_domain.d.ts.map +0 -1
  554. package/scripts/displayAuthorForPost.d.ts.map +0 -1
  555. package/scripts/endorsementFraudDetection/analyseEndorsementsForCommunity.d.ts.map +0 -1
  556. package/scripts/endorsementFraudDetection/bulkDeleteDuplicateEndorsmentsFromUrl.d.ts.map +0 -1
  557. package/scripts/exportAllStatusChanges.d.ts.map +0 -1
  558. package/scripts/exportClientAcitivity.d.ts.map +0 -1
  559. package/scripts/exportEndorsementsForCommunity.d.ts.map +0 -1
  560. package/scripts/exportPointQualitiesForCommunity.d.ts.map +0 -1
  561. package/scripts/exportPostsAndPointsForCommunity.d.ts.map +0 -1
  562. package/scripts/exportPostsDataSetForDomain.d.ts.map +0 -1
  563. package/scripts/exportPostsForGroup.d.ts.map +0 -1
  564. package/scripts/exportRatingsForPost.d.ts.map +0 -1
  565. package/scripts/exportUserEndorsementsWithUserAnalysis.d.ts.map +0 -1
  566. package/scripts/exportUsersForCommunity.d.ts.map +0 -1
  567. package/scripts/exportUsersForDomain.d.ts.map +0 -1
  568. package/scripts/exportUsersForGroup.d.ts.map +0 -1
  569. package/scripts/exports/ratingDistribution.d.ts.map +0 -1
  570. package/scripts/exports/whoEndorsedWhatByCommunity.d.ts.map +0 -1
  571. package/scripts/findUnusedClientImports.d.ts.map +0 -1
  572. package/scripts/fixAllPostPointCounts.d.ts.map +0 -1
  573. package/scripts/fixAnonNotificationsSettings.d.ts.map +0 -1
  574. package/scripts/fixCountKopavogur.d.ts.map +0 -1
  575. package/scripts/fixEndorsementsAfterCopyPostToGroup.d.ts.map +0 -1
  576. package/scripts/fixExternalUserId.d.ts.map +0 -1
  577. package/scripts/fixGroupAccess.d.ts.map +0 -1
  578. package/scripts/fixGroupIdeasAndPointsCount.d.ts.map +0 -1
  579. package/scripts/fixNotificationSettings.d.ts.map +0 -1
  580. package/scripts/fixSurveyRadioBakedInSubCodes.d.ts.map +0 -1
  581. package/scripts/fixWrongUserIdForStatusUpdates.d.ts.map +0 -1
  582. package/scripts/gallery/exportGalleryData.d.ts.map +0 -1
  583. package/scripts/gallery/importGalleryForCommunity.d.ts.map +0 -1
  584. package/scripts/gallery/readJsonAndDownloadImagesVersion2.d.ts.map +0 -1
  585. package/scripts/gallery/refreshAcApiPostIdsForCommunity.d.ts.map +0 -1
  586. package/scripts/genderAnalysis.d.ts.map +0 -1
  587. package/scripts/genderAnalysisByStatus.d.ts.map +0 -1
  588. package/scripts/importAllLocalesFromLocalFolders.d.ts.map +0 -1
  589. package/scripts/importDomain.d.ts.map +0 -1
  590. package/scripts/landUseGame/export3Ddata.d.ts.map +0 -1
  591. package/scripts/listLanguagesForGroup.d.ts.map +0 -1
  592. package/scripts/loadTestCreateDummyContentForGroup.d.ts.map +0 -1
  593. package/scripts/makeRecursiveMapData.d.ts.map +0 -1
  594. package/scripts/mapping/community_map_csv.d.ts.map +0 -1
  595. package/scripts/moveCommunityToDomain.d.ts.map +0 -1
  596. package/scripts/moveGroupToCommunity.d.ts.map +0 -1
  597. package/scripts/movePostToGroup.d.ts.map +0 -1
  598. package/scripts/movePostsToGroupsRecountGroupFromUrl.d.ts.map +0 -1
  599. package/scripts/oldMovePostToGroup.d.ts.map +0 -1
  600. package/scripts/processCsvForPdfUrls.d.ts.map +0 -1
  601. package/scripts/processCsvForTranslationAndToxicity.d.ts.map +0 -1
  602. package/scripts/recount/recount_recursive_communities.d.ts.map +0 -1
  603. package/scripts/recountALLCommunityGroupCounts.d.ts.map +0 -1
  604. package/scripts/recountAll.d.ts.map +0 -1
  605. package/scripts/recountCommunitesFromUrl.d.ts.map +0 -1
  606. package/scripts/recountCommunity.d.ts.map +0 -1
  607. package/scripts/recountGroup.d.ts.map +0 -1
  608. package/scripts/recountGroupNoUserChange.d.ts.map +0 -1
  609. package/scripts/resetAllEndorsementsForGroup.d.ts.map +0 -1
  610. package/scripts/resetEnTranslationForGroup.d.ts.map +0 -1
  611. package/scripts/setDomainLocales.d.ts.map +0 -1
  612. package/scripts/setLanguageOnGroupCommunitesFromUrl.d.ts.map +0 -1
  613. package/scripts/setMemberOfAll.d.ts.map +0 -1
  614. package/scripts/setNewUserForContentOfCommunity.d.ts.map +0 -1
  615. package/scripts/setOfficialStatusOnAllPostsForCommunity.d.ts.map +0 -1
  616. package/scripts/setUserOnAll.d.ts.map +0 -1
  617. package/scripts/showCategoryForGroup.d.ts.map +0 -1
  618. package/scripts/showOldActivityTypes.d.ts.map +0 -1
  619. package/scripts/showPostsMissingCategoryForGroup.d.ts.map +0 -1
  620. package/scripts/showStatuses.d.ts.map +0 -1
  621. package/scripts/showUniqueVotersInCommunity.d.ts.map +0 -1
  622. package/scripts/showUserAgentsAndIpsForEmails.d.ts.map +0 -1
  623. package/scripts/simpleExportForGroupsForCommunity.d.ts.map +0 -1
  624. package/scripts/simpleExportForPointsForCommunity.d.ts.map +0 -1
  625. package/scripts/simpleExportForPostsForCommunity.d.ts.map +0 -1
  626. package/scripts/testForEndorsments.d.ts.map +0 -1
  627. package/scripts/undeleteGroupAndAllContent.d.ts.map +0 -1
  628. package/scripts/undeletePost.d.ts.map +0 -1
  629. package/scripts/unlinkSsn.d.ts.map +0 -1
  630. package/scripts/updateFromAlthingi.d.ts.map +0 -1
  631. package/server.d.ts.map +0 -1
  632. package/utils/copyGroup.js +0 -246
  633. package/utils/loggerTs.d.ts +0 -4
  634. package/utils/loggerTs.d.ts.map +0 -1
  635. package/utils/ypLanguages.d.ts +0 -18
  636. package/utils/ypLanguages.d.ts.map +0 -1
  637. /package/{agents/tools/updateCommunityWorkflow.js → dist/active-citizen/llms/imageGeneration/iImageGenerator.js} +0 -0
  638. /package/{migrations/270724_createUsersAndAdminsForClasses.cjs → dist/migrations/zzzz_createUsersAndAdminsForClasses.cjs} +0 -0
@@ -0,0 +1,4309 @@
1
+ "use strict";
2
+ var express = require("express");
3
+ var router = express.Router();
4
+ var models = require("../models/index.cjs");
5
+ var auth = require("../authorization.cjs");
6
+ var log = require("../utils/logger.cjs");
7
+ var toJson = require("../utils/to_json.cjs");
8
+ var _ = require("lodash");
9
+ var async = require("async");
10
+ var crypto = require("crypto");
11
+ var seededShuffle = require("knuth-shuffle-seeded");
12
+ var multer = require("multer");
13
+ var s3multer = require("multer-s3");
14
+ var aws = require("aws-sdk");
15
+ var getExportFileDataForGroup = require("../utils/export_utils.cjs").getExportFileDataForGroup;
16
+ const exportGroupToDocx = require("../utils/docx_utils.cjs").exportGroupToDocx;
17
+ const { v4: uuidv4 } = require("uuid");
18
+ const { Op, literal } = require("sequelize");
19
+ var moment = require("moment");
20
+ var sanitizeFilename = require("sanitize-filename");
21
+ var queue = require("../active-citizen/workers/queue.cjs");
22
+ const getAllModeratedItemsByGroup = require("../active-citizen/engine/moderation/get_moderation_items.cjs").getAllModeratedItemsByGroup;
23
+ const performSingleModerationAction = require("../active-citizen/engine/moderation/process_moderation_items.cjs").performSingleModerationAction;
24
+ const request = require("request");
25
+ const { updateAnswerTranslation, } = require("../active-citizen/utils/translation_helpers.cjs");
26
+ const { updateSurveyTranslation, } = require("../active-citizen/utils/translation_helpers.cjs");
27
+ const { plausibleStatsProxy, getPlausibleStats, } = require("../active-citizen/engine/analytics/plausible/manager.cjs");
28
+ const { countAllModeratedItemsByGroup, } = require("../active-citizen/engine/moderation/get_moderation_items.cjs");
29
+ const { isValidDbId } = require("../utils/is_valid_db_id.cjs");
30
+ const { Sequelize } = require("sequelize");
31
+ const getFromAnalyticsApi = require("../active-citizen/engine/analytics/manager.cjs").getFromAnalyticsApi;
32
+ const triggerSimilaritiesTraining = require("../active-citizen/engine/analytics/manager.cjs").triggerSimilaritiesTraining;
33
+ const sendBackAnalyticsResultsOrError = require("../active-citizen/engine/analytics/manager.cjs").sendBackAnalyticsResultsOrError;
34
+ const countModelRowsByTimePeriod = require("../active-citizen/engine/analytics/statsCalc.cjs").countModelRowsByTimePeriod;
35
+ const getGroupIncludes = require("../active-citizen/engine/analytics/statsCalc.cjs").getGroupIncludes;
36
+ const getPointGroupIncludes = require("../active-citizen/engine/analytics/statsCalc.cjs").getPointGroupIncludes;
37
+ const getParsedSimilaritiesContent = require("../active-citizen/engine/analytics/manager.cjs").getParsedSimilaritiesContent;
38
+ const getTranslatedTextsForGroup = require("../active-citizen/utils/translation_helpers.cjs").getTranslatedTextsForGroup;
39
+ const updateTranslationForGroup = require("../active-citizen/utils/translation_helpers.cjs").updateTranslationForGroup;
40
+ const convertDocxSurveyToJson = require("../active-citizen/engine/analytics/manager.cjs").convertDocxSurveyToJson;
41
+ const copyGroup = require("../utils/copy_utils.cjs").copyGroup;
42
+ var s3 = new aws.S3({
43
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
44
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
45
+ endpoint: process.env.S3_ENDPOINT || null,
46
+ acl: "public-read",
47
+ region: process.env.S3_REGION || (process.env.S3_ENDPOINT ? null : "us-east-1"),
48
+ });
49
+ var sendGroupOrError = function (res, group, context, user, error, errorStatus) {
50
+ if (error || !group) {
51
+ if (errorStatus === 404 ||
52
+ (error &&
53
+ error.message &&
54
+ error.message.indexOf("invalid input syntax for type integer") > -1)) {
55
+ log.warn("Group Not Found", {
56
+ context: context,
57
+ group: toJson(group),
58
+ user: toJson(user),
59
+ err: error,
60
+ errorStatus: 404,
61
+ });
62
+ errorStatus = 404;
63
+ }
64
+ else {
65
+ log.error("Group Error", {
66
+ context: context,
67
+ group: toJson(group),
68
+ user: toJson(user),
69
+ err: error,
70
+ errorStatus: errorStatus ? errorStatus : 500,
71
+ });
72
+ }
73
+ if (errorStatus) {
74
+ res.sendStatus(errorStatus);
75
+ }
76
+ else {
77
+ res.sendStatus(500);
78
+ }
79
+ }
80
+ else {
81
+ res.send(group);
82
+ }
83
+ };
84
+ var getGroupAndUser = function (groupId, userId, userEmail, callback) {
85
+ var user, group;
86
+ async.series([
87
+ function (seriesCallback) {
88
+ models.Group.findOne({
89
+ where: {
90
+ id: groupId,
91
+ },
92
+ attributes: models.Group.defaultAttributesPublic,
93
+ })
94
+ .then(function (groupIn) {
95
+ if (groupIn) {
96
+ group = groupIn;
97
+ }
98
+ seriesCallback();
99
+ })
100
+ .catch(function (error) {
101
+ seriesCallback(error);
102
+ });
103
+ },
104
+ function (seriesCallback) {
105
+ if (userId) {
106
+ models.User.findOne({
107
+ where: {
108
+ id: userId,
109
+ },
110
+ attributes: ["id", "email", "name", "created_at"],
111
+ })
112
+ .then(function (userIn) {
113
+ if (userIn) {
114
+ user = userIn;
115
+ }
116
+ seriesCallback();
117
+ })
118
+ .catch(function (error) {
119
+ seriesCallback(error);
120
+ });
121
+ }
122
+ else {
123
+ seriesCallback();
124
+ }
125
+ },
126
+ function (seriesCallback) {
127
+ if (userEmail) {
128
+ models.User.findOne({
129
+ where: {
130
+ email: userEmail,
131
+ },
132
+ attributes: ["id", "email", "name", "created_at"],
133
+ })
134
+ .then(function (userIn) {
135
+ if (userIn) {
136
+ user = userIn;
137
+ }
138
+ seriesCallback();
139
+ })
140
+ .catch(function (error) {
141
+ seriesCallback(error);
142
+ });
143
+ }
144
+ else {
145
+ seriesCallback();
146
+ }
147
+ },
148
+ ], function (error) {
149
+ if (error) {
150
+ callback(error);
151
+ }
152
+ else {
153
+ callback(null, group, user);
154
+ }
155
+ });
156
+ };
157
+ var truthValueFromBody = function (bodyParameter) {
158
+ if (bodyParameter && bodyParameter != "") {
159
+ return true;
160
+ }
161
+ else {
162
+ return false;
163
+ }
164
+ };
165
+ const moveGroupTo = (req, group) => {
166
+ const splitMoveTo = req.body.moveGroupTo.split(" - ");
167
+ const id = splitMoveTo[0];
168
+ if (id && id.indexOf("C") > -1) {
169
+ group.set("in_group_folder_id", null);
170
+ }
171
+ else if (!isNaN(id)) {
172
+ if (parseInt(id) !== 0) {
173
+ group.set("in_group_folder_id", parseInt(id));
174
+ }
175
+ }
176
+ };
177
+ var updateGroupConfigParameters = function (req, group) {
178
+ if (!group.configuration) {
179
+ group.set("configuration", {});
180
+ }
181
+ group.set("configuration.canVote", truthValueFromBody(req.body.canVote));
182
+ group.set("configuration.canAddNewPosts", truthValueFromBody(req.body.canAddNewPosts));
183
+ group.set("configuration.disableDebate", truthValueFromBody(req.body.disableDebate));
184
+ group.set("configuration.locationHidden", truthValueFromBody(req.body.locationHidden));
185
+ group.set("configuration.showWhoPostedPosts", truthValueFromBody(req.body.showWhoPostedPosts));
186
+ group.set("configuration.allowAnonymousUsers", truthValueFromBody(req.body.allowAnonymousUsers));
187
+ group.set("configuration.allowAnonymousAutoLogin", truthValueFromBody(req.body.allowAnonymousAutoLogin));
188
+ group.set("configuration.anonymousAskRegistrationQuestions", truthValueFromBody(req.body.anonymousAskRegistrationQuestions));
189
+ group.set("configuration.hideAllTabs", truthValueFromBody(req.body.hideAllTabs));
190
+ group.set("configuration.hideNewPostOnPostPage", truthValueFromBody(req.body.hideNewPostOnPostPage));
191
+ group.set("configuration.newPointOptional", truthValueFromBody(req.body.newPointOptional));
192
+ group.set("configuration.hideHelpIcon", truthValueFromBody(req.body.hideHelpIcon));
193
+ group.set("configuration.hideEmoji", truthValueFromBody(req.body.hideEmoji));
194
+ group.set("configuration.hideGroupHeader", truthValueFromBody(req.body.hideGroupHeader));
195
+ group.set("configuration.hidePointAuthor", truthValueFromBody(req.body.hidePointAuthor));
196
+ group.set("configuration.hidePostAuthor", truthValueFromBody(req.body.hidePostAuthor));
197
+ group.set("configuration.hideDownVoteForPost", truthValueFromBody(req.body.hideDownVoteForPost));
198
+ group.set("configuration.attachmentsEnabled", truthValueFromBody(req.body.attachmentsEnabled));
199
+ group.set("configuration.moreContactInformation", truthValueFromBody(req.body.moreContactInformation));
200
+ group.set("configuration.moreContactInformationAddress", truthValueFromBody(req.body.moreContactInformationAddress));
201
+ group.set("configuration.useContainImageMode", truthValueFromBody(req.body.useContainImageMode));
202
+ group.set("configuration.endorsementButtons", req.body.endorsementButtons && req.body.endorsementButtons != ""
203
+ ? req.body.endorsementButtons
204
+ : "hearts");
205
+ group.set("configuration.alternativeHeader", req.body.alternativeHeader && req.body.alternativeHeader != ""
206
+ ? req.body.alternativeHeader
207
+ : null);
208
+ group.set("configuration.defaultLocationLongLat", req.body.defaultLocationLongLat && req.body.defaultLocationLongLat != ""
209
+ ? req.body.defaultLocationLongLat
210
+ : null);
211
+ group.set("configuration.postDescriptionLimit", req.body.postDescriptionLimit && req.body.postDescriptionLimit != ""
212
+ ? req.body.postDescriptionLimit
213
+ : "500");
214
+ if (truthValueFromBody(req.body.status)) {
215
+ group.status = req.body.status;
216
+ }
217
+ if (truthValueFromBody(req.body.defaultLocale)) {
218
+ group.set("configuration.defaultLocale", req.body.defaultLocale);
219
+ }
220
+ if (truthValueFromBody(req.body.uploadedDefaultDataImageId)) {
221
+ group.set("configuration.defaultDataImageId", req.body.uploadedDefaultDataImageId);
222
+ }
223
+ if (truthValueFromBody(req.body.uploadedDefaultPostImageId)) {
224
+ group.set("configuration.uploadedDefaultPostImageId", req.body.uploadedDefaultPostImageId);
225
+ }
226
+ group.set("configuration.alternativeTextForNewIdeaButton", req.body.alternativeTextForNewIdeaButton &&
227
+ req.body.alternativeTextForNewIdeaButton !== ""
228
+ ? req.body.alternativeTextForNewIdeaButton
229
+ : null);
230
+ group.set("configuration.alternativeTextForNewIdeaButtonClosed", req.body.alternativeTextForNewIdeaButtonClosed &&
231
+ req.body.alternativeTextForNewIdeaButtonClosed !== ""
232
+ ? req.body.alternativeTextForNewIdeaButtonClosed
233
+ : null);
234
+ group.set("configuration.alternativeTextForNewIdeaButtonHeader", req.body.alternativeTextForNewIdeaButtonHeader &&
235
+ req.body.alternativeTextForNewIdeaButtonHeader !== ""
236
+ ? req.body.alternativeTextForNewIdeaButtonHeader
237
+ : null);
238
+ group.set("configuration.alternativeTextForNewIdeaSaveButton", req.body.alternativeTextForNewIdeaSaveButton &&
239
+ req.body.alternativeTextForNewIdeaSaveButton !== ""
240
+ ? req.body.alternativeTextForNewIdeaSaveButton
241
+ : null);
242
+ group.set("configuration.customCategoryQuestionText", req.body.customCategoryQuestionText &&
243
+ req.body.customCategoryQuestionText !== ""
244
+ ? req.body.customCategoryQuestionText
245
+ : null);
246
+ group.set("configuration.alternativePointForHeader", req.body.alternativePointForHeader &&
247
+ req.body.alternativePointForHeader != ""
248
+ ? req.body.alternativePointForHeader
249
+ : null);
250
+ group.set("configuration.alternativePointAgainstHeader", req.body.alternativePointAgainstHeader &&
251
+ req.body.alternativePointAgainstHeader != ""
252
+ ? req.body.alternativePointAgainstHeader
253
+ : null);
254
+ group.set("configuration.alternativePointForLabel", req.body.alternativePointForLabel && req.body.alternativePointForLabel != ""
255
+ ? req.body.alternativePointForLabel
256
+ : null);
257
+ group.set("configuration.alternativePointAgainstLabel", req.body.alternativePointAgainstLabel &&
258
+ req.body.alternativePointAgainstLabel != ""
259
+ ? req.body.alternativePointAgainstLabel
260
+ : null);
261
+ group.set("configuration.disableFacebookLoginForGroup", truthValueFromBody(req.body.disableFacebookLoginForGroup));
262
+ group.set("configuration.disableNameAutoTranslation", truthValueFromBody(req.body.disableNameAutoTranslation));
263
+ group.set("configuration.externalGoalTriggerUrl", req.body.externalGoalTriggerUrl && req.body.externalGoalTriggerUrl != ""
264
+ ? req.body.externalGoalTriggerUrl
265
+ : null);
266
+ group.set("configuration.hideNewPost", truthValueFromBody(req.body.hideNewPost));
267
+ group.set("configuration.disableCollectionUpLink", truthValueFromBody(req.body.disableCollectionUpLink));
268
+ group.set("configuration.disableCommunityUpLink", truthValueFromBody(req.body.disableCommunityUpLink));
269
+ group.set("configuration.makeCategoryRequiredOnNewPost", truthValueFromBody(req.body.makeCategoryRequiredOnNewPost));
270
+ group.set("configuration.showVideoUploadDisclaimer", truthValueFromBody(req.body.showVideoUploadDisclaimer));
271
+ group.set("configuration.welcomePageId", req.body.welcomePageId && req.body.welcomePageId != ""
272
+ ? req.body.welcomePageId
273
+ : null);
274
+ group.set("configuration.hideVoteCount", truthValueFromBody(req.body.hideVoteCount));
275
+ group.set("configuration.hideGroupType", truthValueFromBody(req.body.hideGroupType));
276
+ group.set("configuration.hideVoteCountUntilVoteCompleted", truthValueFromBody(req.body.hideVoteCountUntilVoteCompleted));
277
+ group.set("configuration.hidePostCover", truthValueFromBody(req.body.hidePostCover));
278
+ group.set("configuration.hidePostDescription", truthValueFromBody(req.body.hidePostDescription));
279
+ group.set("configuration.hideDebateIcon", truthValueFromBody(req.body.hideDebateIcon));
280
+ group.set("configuration.hideSharing", truthValueFromBody(req.body.hideSharing));
281
+ group.set("configuration.hidePointAgainst", truthValueFromBody(req.body.hidePointAgainst));
282
+ group.set("configuration.disablePostPageLink", truthValueFromBody(req.body.disablePostPageLink));
283
+ group.set("configuration.hidePostActionsInGrid", truthValueFromBody(req.body.hidePostActionsInGrid));
284
+ group.set("configuration.forceSecureSamlLogin", truthValueFromBody(req.body.forceSecureSamlLogin));
285
+ group.set("configuration.forceSecureSamlEmployeeLogin", truthValueFromBody(req.body.forceSecureSamlEmployeeLogin));
286
+ group.set("configuration.galleryMode", truthValueFromBody(req.body.galleryMode));
287
+ group.set("configuration.showNameUnderLogoImage", truthValueFromBody(req.body.showNameUnderLogoImage));
288
+ group.set("configuration.alwaysHideLogoImage", truthValueFromBody(req.body.alwaysHideLogoImage));
289
+ group.set("configuration.hideStatsAndMembership", truthValueFromBody(req.body.hideStatsAndMembership));
290
+ group.set("configuration.centerGroupName", truthValueFromBody(req.body.centerGroupName));
291
+ group.set("configuration.noGroupCardShadow", truthValueFromBody(req.body.noGroupCardShadow));
292
+ group.set("configuration.hideNewestFromFilter", truthValueFromBody(req.body.hideNewestFromFilter));
293
+ group.set("configuration.hideGroupLevelTabs", truthValueFromBody(req.body.hideGroupLevelTabs));
294
+ group.set("configuration.hidePostFilterAndSearch", truthValueFromBody(req.body.hidePostFilterAndSearch));
295
+ group.set("configuration.hidePostImageUploads", truthValueFromBody(req.body.hidePostImageUploads));
296
+ group.set("configuration.hideNewPointOnNewIdea", truthValueFromBody(req.body.hideNewPointOnNewIdea));
297
+ group.set("configuration.maxDaysBackForRecommendations", req.body.maxDaysBackForRecommendations &&
298
+ req.body.maxDaysBackForRecommendations != ""
299
+ ? req.body.maxDaysBackForRecommendations
300
+ : null);
301
+ group.set("configuration.groupType", req.body.groupType && req.body.groupType != "" ? req.body.groupType : null);
302
+ group.set("configuration.externalId", req.body.externalId && req.body.externalId != ""
303
+ ? req.body.externalId
304
+ : null);
305
+ group.set("configuration.usePostListFormatOnDesktop", truthValueFromBody(req.body.usePostListFormatOnDesktop));
306
+ group.set("configuration.usePostTagsForPostListItems", truthValueFromBody(req.body.usePostTagsForPostListItems));
307
+ group.set("configuration.usePostTagsForPostCards", truthValueFromBody(req.body.usePostTagsForPostCards));
308
+ group.set("configuration.usePostTags", truthValueFromBody(req.body.usePostTags));
309
+ group.set("configuration.closeNewsfeedSubmissions", truthValueFromBody(req.body.closeNewsfeedSubmissions));
310
+ group.set("configuration.hideNewsfeeds", truthValueFromBody(req.body.hideNewsfeeds));
311
+ group.set("configuration.allowGenerativeImages", truthValueFromBody(req.body.allowGenerativeImages));
312
+ group.set("configuration.askUserIfNameShouldBeDisplayed", truthValueFromBody(req.body.askUserIfNameShouldBeDisplayed));
313
+ group.set("configuration.allowPostVideoUploads", truthValueFromBody(req.body.allowPostVideoUploads));
314
+ group.set("configuration.allowPointVideoUploads", truthValueFromBody(req.body.allowPointVideoUploads));
315
+ group.set("configuration.useVideoCover", truthValueFromBody(req.body.useVideoCover));
316
+ group.set("configuration.videoPostUploadLimitSec", req.body.videoPostUploadLimitSec && req.body.videoPostUploadLimitSec != ""
317
+ ? req.body.videoPostUploadLimitSec
318
+ : "60");
319
+ group.set("configuration.videoPointUploadLimitSec", req.body.videoPointUploadLimitSec && req.body.videoPointUploadLimitSec != ""
320
+ ? req.body.videoPointUploadLimitSec
321
+ : "30");
322
+ if (group.configuration.videoPostUploadLimitSec &&
323
+ parseInt(group.configuration.videoPostUploadLimitSec)) {
324
+ if (parseInt(group.configuration.videoPostUploadLimitSec) > 600) {
325
+ group.set("configuration.videoPostUploadLimitSec", 600);
326
+ }
327
+ }
328
+ if (group.configuration.videoPointUploadLimitSec &&
329
+ parseInt(group.configuration.videoPointUploadLimitSec)) {
330
+ if (parseInt(group.configuration.videoPointUploadLimitSec) > 600) {
331
+ group.set("configuration.videoPointUploadLimitSec", 600);
332
+ }
333
+ }
334
+ group.set("configuration.customTitleQuestionText", req.body.customTitleQuestionText && req.body.customTitleQuestionText != ""
335
+ ? req.body.customTitleQuestionText
336
+ : null);
337
+ group.set("configuration.customFilterText", req.body.customFilterText && req.body.customFilterText != ""
338
+ ? req.body.customFilterText
339
+ : null);
340
+ group.set("configuration.customBackURL", req.body.customBackURL && req.body.customBackURL != ""
341
+ ? req.body.customBackURL
342
+ : null);
343
+ group.set("configuration.customBackName", req.body.customBackName && req.body.customBackName != ""
344
+ ? req.body.customBackName
345
+ : null);
346
+ group.set("configuration.customVoteUpHoverText", req.body.customVoteUpHoverText && req.body.customVoteUpHoverText != ""
347
+ ? req.body.customVoteUpHoverText
348
+ : null);
349
+ group.set("configuration.customVoteDownHoverText", req.body.customVoteDownHoverText && req.body.customVoteDownHoverText != ""
350
+ ? req.body.customVoteDownHoverText
351
+ : null);
352
+ group.set("configuration.hideRecommendationOnNewsFeed", truthValueFromBody(req.body.hideRecommendationOnNewsFeed));
353
+ group.set("configuration.useAsTemplate", truthValueFromBody(req.body.useAsTemplate));
354
+ group.set("configuration.disableMachineTranscripts", truthValueFromBody(req.body.disableMachineTranscripts));
355
+ group.set("configuration.hideLogoBoxExceptOnMobile", truthValueFromBody(req.body.hideLogoBoxExceptOnMobile));
356
+ group.set("configuration.hideInfoBoxExceptForAdmins", truthValueFromBody(req.body.hideInfoBoxExceptForAdmins));
357
+ group.set("configuration.hideLogoBoxShadow", truthValueFromBody(req.body.hideLogoBoxShadow));
358
+ group.set("configuration.descriptionTruncateAmount", req.body.descriptionTruncateAmount &&
359
+ req.body.descriptionTruncateAmount != ""
360
+ ? req.body.descriptionTruncateAmount
361
+ : null);
362
+ group.set("configuration.descriptionSimpleFormat", truthValueFromBody(req.body.descriptionSimpleFormat));
363
+ group.set("configuration.hidePointForAgainstIcons", truthValueFromBody(req.body.hidePointForAgainstIcons));
364
+ group.set("configuration.transcriptSimpleFormat", truthValueFromBody(req.body.transcriptSimpleFormat));
365
+ group.set("configuration.allowPostAudioUploads", truthValueFromBody(req.body.allowPostAudioUploads));
366
+ group.set("configuration.allowPointAudioUploads", truthValueFromBody(req.body.allowPointAudioUploads));
367
+ group.set("configuration.useAudioCover", truthValueFromBody(req.body.useAudioCover));
368
+ group.set("configuration.audioPostUploadLimitSec", req.body.audioPostUploadLimitSec && req.body.audioPostUploadLimitSec != ""
369
+ ? req.body.audioPostUploadLimitSec
370
+ : "60");
371
+ group.set("configuration.audioPointUploadLimitSec", req.body.audioPointUploadLimitSec && req.body.audioPointUploadLimitSec != ""
372
+ ? req.body.audioPointUploadLimitSec
373
+ : "30");
374
+ if (group.configuration.audioPostUploadLimitSec &&
375
+ parseInt(group.configuration.audioPostUploadLimitSec)) {
376
+ if (parseInt(group.configuration.audioPostUploadLimitSec) > 600) {
377
+ group.set("configuration.audioPostUploadLimitSec", 600);
378
+ }
379
+ }
380
+ group.set("configuration.maxNumberOfGroupVotes", req.body.maxNumberOfGroupVotes && req.body.maxNumberOfGroupVotes != ""
381
+ ? req.body.maxNumberOfGroupVotes
382
+ : null);
383
+ if (group.configuration.audioPointUploadLimitSec &&
384
+ parseInt(group.configuration.audioPointUploadLimitSec)) {
385
+ if (parseInt(group.configuration.audioPointUploadLimitSec) > 600) {
386
+ group.set("configuration.audioPointUploadLimitSec", 600);
387
+ }
388
+ }
389
+ group.set("configuration.urlToReviewActionText", req.body.urlToReviewActionText && req.body.urlToReviewActionText != ""
390
+ ? req.body.urlToReviewActionText
391
+ : null);
392
+ group.set("configuration.structuredQuestions", req.body.structuredQuestions && req.body.structuredQuestions != ""
393
+ ? req.body.structuredQuestions
394
+ : null);
395
+ if (group.configuration.structuredQuestions) {
396
+ try {
397
+ const cleaned = group.configuration.structuredQuestions
398
+ .trim()
399
+ .replace(/\n/g, "")
400
+ .replace(/\r/g, "")
401
+ .replace(/"/, '"');
402
+ const jsonArray = JSON.parse(cleaned);
403
+ const updatedJsonArray = [];
404
+ let questionIndex = 0;
405
+ jsonArray.forEach((question, index) => {
406
+ if (question.type.toLowerCase() === "textfield" ||
407
+ question.type.toLowerCase() === "textfieldlong" ||
408
+ question.type.toLowerCase() === "textarea" ||
409
+ question.type.toLowerCase() === "textarealong" ||
410
+ question.type.toLowerCase() === "numberfield" ||
411
+ question.type.toLowerCase() === "checkboxes" ||
412
+ question.type.toLowerCase() === "radios" ||
413
+ question.type.toLowerCase() === "dropdown") {
414
+ question.questionIndex = questionIndex += 1;
415
+ }
416
+ updatedJsonArray.push(question);
417
+ });
418
+ group.set("configuration.structuredQuestionsJson", updatedJsonArray);
419
+ }
420
+ catch (error) {
421
+ group.set("configuration.structuredQuestionsJson", null);
422
+ log.error("Error in parsing structured questions", { error });
423
+ }
424
+ }
425
+ else {
426
+ group.set("configuration.structuredQuestionsJson", null);
427
+ }
428
+ group.set("configuration.registrationQuestions", req.body.registrationQuestions && req.body.registrationQuestions != ""
429
+ ? req.body.registrationQuestions
430
+ : null);
431
+ if (group.configuration.registrationQuestions) {
432
+ try {
433
+ const cleaned = group.configuration.registrationQuestions
434
+ .trim()
435
+ .replace(/\n/g, "")
436
+ .replace(/\r/g, "");
437
+ const jsonArray = JSON.parse(cleaned);
438
+ group.set("configuration.registrationQuestionsJson", jsonArray);
439
+ }
440
+ catch (error) {
441
+ group.set("configuration.registrationQuestionsJson", null);
442
+ log.error("Error in parsing registrationQuestions", { error });
443
+ }
444
+ }
445
+ else {
446
+ group.set("configuration.registrationQuestionsJson", null);
447
+ }
448
+ group.set("configuration.isDataVisualizationGroup", truthValueFromBody(req.body.isDataVisualizationGroup));
449
+ group.set("configuration.dataForVisualization", req.body.dataForVisualization && req.body.dataForVisualization != ""
450
+ ? req.body.dataForVisualization
451
+ : null);
452
+ if (group.configuration.dataForVisualization) {
453
+ try {
454
+ const cleaned = group.configuration.dataForVisualization
455
+ .trim()
456
+ .replace(/\n/g, "")
457
+ .replace(/\r/g, "");
458
+ const jsonArray = JSON.parse(cleaned);
459
+ group.set("configuration.dataForVisualizationJson", jsonArray);
460
+ }
461
+ catch (error) {
462
+ group.set("configuration.dataForVisualizationJson", null);
463
+ log.error("Error in parsing dataForVisualization", { error });
464
+ }
465
+ }
466
+ else {
467
+ group.set("configuration.dataForVisualizationJson", null);
468
+ }
469
+ const ltpConfigText = req.body.ltp && req.body.ltp != "" ? req.body.ltp : null;
470
+ if (ltpConfigText) {
471
+ try {
472
+ const cleaned = ltpConfigText
473
+ .trim()
474
+ .replace(/\n/g, "")
475
+ .replace(/\r/g, "");
476
+ const parsedJson = JSON.parse(cleaned);
477
+ group.set("configuration.ltp", parsedJson);
478
+ }
479
+ catch (error) {
480
+ group.set("configuration.ltp", null);
481
+ log.error("Error in parsing ltp", { error });
482
+ }
483
+ }
484
+ else {
485
+ group.set("configuration.ltp", null);
486
+ }
487
+ const allOurIdeas = req.body.allOurIdeas && req.body.allOurIdeas != ""
488
+ ? req.body.allOurIdeas
489
+ : null;
490
+ if (allOurIdeas) {
491
+ try {
492
+ const cleaned = allOurIdeas.trim().replace(/\n/g, "").replace(/\r/g, "");
493
+ const parsedJson = JSON.parse(cleaned);
494
+ group.set("configuration.allOurIdeas", parsedJson);
495
+ }
496
+ catch (error) {
497
+ group.set("configuration.allOurIdeas", null);
498
+ log.error("Error in parsing allOurIdeas", { error });
499
+ }
500
+ }
501
+ else {
502
+ group.set("configuration.allOurIdeas", null);
503
+ }
504
+ const staticHtml = req.body.staticHtml && req.body.staticHtml != ""
505
+ ? req.body.staticHtml
506
+ : null;
507
+ if (staticHtml) {
508
+ try {
509
+ const cleaned = staticHtml.trim().replace(/\n/g, "").replace(/\r/g, "");
510
+ const parsedJson = JSON.parse(cleaned);
511
+ group.set("configuration.staticHtml", parsedJson);
512
+ }
513
+ catch (error) {
514
+ group.set("configuration.staticHtml", undefined);
515
+ log.error("Error in parsing staticHtml", { error });
516
+ }
517
+ }
518
+ else {
519
+ group.set("configuration.staticHtml", undefined);
520
+ }
521
+ const theme = req.body.theme && req.body.theme != "" ? req.body.theme : null;
522
+ if (theme) {
523
+ try {
524
+ const cleaned = theme.trim().replace(/\n/g, "").replace(/\r/g, "");
525
+ const parsedJson = JSON.parse(cleaned);
526
+ group.set("configuration.theme", parsedJson);
527
+ }
528
+ catch (error) {
529
+ group.set("configuration.theme", null);
530
+ log.error("Error in parsing theme", { error });
531
+ }
532
+ }
533
+ else {
534
+ group.set("configuration.theme", null);
535
+ }
536
+ group.set("configuration.themeOverrideColorPrimary", req.body.themeOverrideColorPrimary &&
537
+ req.body.themeOverrideColorPrimary != ""
538
+ ? req.body.themeOverrideColorPrimary
539
+ : null);
540
+ group.set("configuration.themeOverrideColorAccent", req.body.themeOverrideColorAccent && req.body.themeOverrideColorAccent != ""
541
+ ? req.body.themeOverrideColorAccent
542
+ : null);
543
+ group.set("configuration.customUserNamePrompt", req.body.customUserNamePrompt && req.body.customUserNamePrompt != ""
544
+ ? req.body.customUserNamePrompt
545
+ : null);
546
+ group.set("configuration.customTermsIntroText", req.body.customTermsIntroText && req.body.customTermsIntroText != ""
547
+ ? req.body.customTermsIntroText
548
+ : null);
549
+ const customRatingsText = req.body.customRatingsText && req.body.customRatingsText != ""
550
+ ? req.body.customRatingsText
551
+ : null;
552
+ group.set("configuration.customRatingsText", customRatingsText);
553
+ if (customRatingsText) {
554
+ var ratingsComponents = customRatingsText.split(",");
555
+ let ratings = [];
556
+ if (ratingsComponents && ratingsComponents.length > 2) {
557
+ for (var i = 0; i < ratingsComponents.length; i += 3) {
558
+ ratings.push({
559
+ name: ratingsComponents[i],
560
+ numberOf: ratingsComponents[i + 1],
561
+ emoji: ratingsComponents[i + 2],
562
+ });
563
+ }
564
+ group.set("configuration.customRatings", ratings);
565
+ }
566
+ else {
567
+ log.error("Ratings not in correct format for customRatings");
568
+ }
569
+ }
570
+ else {
571
+ group.set("configuration.customRatings", null);
572
+ }
573
+ group.set("configuration.customTabTitleNewLocation", req.body.customTabTitleNewLocation &&
574
+ req.body.customTabTitleNewLocation !== ""
575
+ ? req.body.customTabTitleNewLocation
576
+ : null);
577
+ group.set("configuration.allowAdminsToDebate", truthValueFromBody(req.body.allowAdminsToDebate));
578
+ group.set("configuration.allowAdminAnswersToPoints", truthValueFromBody(req.body.allowAdminAnswersToPoints));
579
+ group.set("configuration.forcePostSortMethodAs", req.body.forcePostSortMethodAs && req.body.forcePostSortMethodAs !== ""
580
+ ? req.body.forcePostSortMethodAs
581
+ : null);
582
+ group.set("configuration.pointCharLimit", req.body.pointCharLimit && req.body.pointCharLimit !== ""
583
+ ? req.body.pointCharLimit
584
+ : null);
585
+ group.set("configuration.allPostsBlockedByDefault", truthValueFromBody(req.body.allPostsBlockedByDefault));
586
+ group.set("configuration.customThankYouTextNewPosts", req.body.customThankYouTextNewPosts &&
587
+ req.body.customThankYouTextNewPosts !== ""
588
+ ? req.body.customThankYouTextNewPosts
589
+ : null);
590
+ group.set("configuration.useCommunityTopBanner", truthValueFromBody(req.body.useCommunityTopBanner));
591
+ group.set("configuration.makeMapViewDefault", truthValueFromBody(req.body.makeMapViewDefault));
592
+ group.set("configuration.allowOneTimeLoginWithName", truthValueFromBody(req.body.allowOneTimeLoginWithName));
593
+ group.set("configuration.simpleFormatDescription", truthValueFromBody(req.body.simpleFormatDescription));
594
+ group.set("configuration.resourceLibraryLinkMode", truthValueFromBody(req.body.resourceLibraryLinkMode));
595
+ group.set("configuration.collapsableTranscripts", truthValueFromBody(req.body.collapsableTranscripts));
596
+ group.set("configuration.customAdminCommentsTitle", req.body.customAdminCommentsTitle &&
597
+ req.body.customAdminCommentsTitle !== ""
598
+ ? req.body.customAdminCommentsTitle
599
+ : null);
600
+ group.set("configuration.themeOverrideBackgroundColor", req.body.themeOverrideBackgroundColor &&
601
+ req.body.themeOverrideBackgroundColor != ""
602
+ ? req.body.themeOverrideBackgroundColor
603
+ : null);
604
+ group.set("configuration.hideNameInputAndReplaceWith", req.body.hideNameInputAndReplaceWith &&
605
+ req.body.hideNameInputAndReplaceWith != ""
606
+ ? req.body.hideNameInputAndReplaceWith
607
+ : null);
608
+ group.set("configuration.hideMediaInput", truthValueFromBody(req.body.hideMediaInput));
609
+ group.set("configuration.actAsLinkToCommunityId", req.body.actAsLinkToCommunityId && req.body.actAsLinkToCommunityId != ""
610
+ ? req.body.actAsLinkToCommunityId
611
+ : null);
612
+ group.set("configuration.hideQuestionIndexOnNewPost", truthValueFromBody(req.body.hideQuestionIndexOnNewPost));
613
+ group.set("configuration.allowWhatsAppSharing", truthValueFromBody(req.body.allowWhatsAppSharing));
614
+ group.set("configuration.inheritThemeFromCommunity", truthValueFromBody(req.body.inheritThemeFromCommunity));
615
+ if (truthValueFromBody(req.body.inheritThemeFromCommunity) === true) {
616
+ group.set("theme_id", null);
617
+ }
618
+ group.set("configuration.optionalSortOrder", req.body.optionalSortOrder && req.body.optionalSortOrder != ""
619
+ ? req.body.optionalSortOrder
620
+ : null);
621
+ group.set("configuration.exportSubCodesForRadiosAndCheckboxes", truthValueFromBody(req.body.exportSubCodesForRadiosAndCheckboxes));
622
+ group.set("configuration.forceShowDebateCountOnPost", truthValueFromBody(req.body.forceShowDebateCountOnPost));
623
+ if (req.body.forAgentId &&
624
+ req.body.forAgentId != "" &&
625
+ req.body.inputOutput &&
626
+ req.body.inputOutput != "") {
627
+ group.set("configuration.agents", {
628
+ inputConnectorForAgentId: req.body.inputOutput == "input" ? req.body.forAgentId : undefined,
629
+ outputConnectorForAgentId: req.body.inputOutput == "output" ? req.body.forAgentId : undefined,
630
+ });
631
+ }
632
+ };
633
+ const getGroupFolder = function (req, done) {
634
+ var groupFolder;
635
+ log.info("getGroupFolder");
636
+ async.series([
637
+ function (seriesCallback) {
638
+ models.Group.findOne({
639
+ where: {
640
+ id: req.params.groupFolderId,
641
+ is_group_folder: true,
642
+ },
643
+ attributes: models.Group.defaultAttributesPublic,
644
+ required: false,
645
+ order: [
646
+ ["counter_users", "desc"],
647
+ [
648
+ { model: models.Image, as: "GroupLogoImages" },
649
+ "created_at",
650
+ "asc",
651
+ ],
652
+ [
653
+ { model: models.Image, as: "GroupHeaderImages" },
654
+ "created_at",
655
+ "asc",
656
+ ],
657
+ [{ model: models.Category }, "name", "asc"],
658
+ ],
659
+ include: models.Group.masterGroupIncludes(models),
660
+ })
661
+ .then(function (group) {
662
+ groupFolder = group;
663
+ if (groupFolder) {
664
+ log.info("Group Folder Viewed", {
665
+ groupFolderId: groupFolder.id,
666
+ userId: req.user ? req.user.id : -1,
667
+ });
668
+ models.Group.addVideosAndCommunityLinksToGroups([groupFolder], (videoError) => {
669
+ seriesCallback(videoError);
670
+ });
671
+ }
672
+ else {
673
+ seriesCallback("Not found");
674
+ }
675
+ return null;
676
+ })
677
+ .catch(function (error) {
678
+ seriesCallback(error);
679
+ });
680
+ },
681
+ function (seriesCallback) {
682
+ const redisKey = "cache:groups_folder:" +
683
+ groupFolder.id +
684
+ ":" +
685
+ models.Group.ACCESS_SECRET;
686
+ req.redisClient
687
+ .get(redisKey)
688
+ .then((groups) => {
689
+ if (groups) {
690
+ groupFolder.dataValues.Groups = JSON.parse(groups);
691
+ seriesCallback();
692
+ }
693
+ else {
694
+ models.Group.findAll({
695
+ where: {
696
+ in_group_folder_id: groupFolder.id,
697
+ access: {
698
+ $ne: models.Group.ACCESS_SECRET,
699
+ },
700
+ status: {
701
+ $ne: "hidden",
702
+ },
703
+ },
704
+ attributes: models.Group.defaultAttributesPublic,
705
+ required: false,
706
+ order: [
707
+ ["counter_users", "desc"],
708
+ [
709
+ { model: models.Image, as: "GroupLogoImages" },
710
+ "created_at",
711
+ "asc",
712
+ ],
713
+ [
714
+ { model: models.Image, as: "GroupHeaderImages" },
715
+ "created_at",
716
+ "asc",
717
+ ],
718
+ [{ model: models.Category }, "name", "asc"],
719
+ ],
720
+ include: models.Group.masterGroupIncludes(models),
721
+ })
722
+ .then(function (groups) {
723
+ groupFolder.dataValues.Groups = groups;
724
+ req.redisClient.setEx(redisKey, process.env.GROUPS_CACHE_TTL
725
+ ? parseInt(process.env.GROUPS_CACHE_TTL)
726
+ : 3, JSON.stringify(groups));
727
+ seriesCallback();
728
+ })
729
+ .catch((error) => {
730
+ seriesCallback(error);
731
+ });
732
+ }
733
+ })
734
+ .catch((error) => {
735
+ seriesCallback(error);
736
+ });
737
+ },
738
+ function (seriesCallback) {
739
+ if (req.user && groupFolder) {
740
+ var adminGroups, userGroups;
741
+ async.parallel([
742
+ function (parallelCallback) {
743
+ models.Group.findAll({
744
+ where: {
745
+ in_group_folder_id: groupFolder.id,
746
+ },
747
+ attributes: models.Group.defaultAttributesPublic,
748
+ order: [
749
+ ["counter_users", "desc"],
750
+ [
751
+ { model: models.Image, as: "GroupLogoImages" },
752
+ "created_at",
753
+ "asc",
754
+ ],
755
+ [
756
+ { model: models.Image, as: "GroupHeaderImages" },
757
+ "created_at",
758
+ "asc",
759
+ ],
760
+ [{ model: models.Category }, "name", "asc"],
761
+ ],
762
+ include: [
763
+ {
764
+ model: models.User,
765
+ as: "GroupAdmins",
766
+ attributes: ["id"],
767
+ required: true,
768
+ through: { attributes: [] },
769
+ where: {
770
+ id: req.user.id,
771
+ },
772
+ },
773
+ ].concat(models.Group.masterGroupIncludes(models)),
774
+ })
775
+ .then(function (groups) {
776
+ adminGroups = groups;
777
+ parallelCallback(null, "admin");
778
+ })
779
+ .catch(function (error) {
780
+ parallelCallback(error);
781
+ });
782
+ },
783
+ function (parallelCallback) {
784
+ models.Group.findAll({
785
+ where: {
786
+ in_group_folder_id: groupFolder.id,
787
+ },
788
+ attributes: models.Group.defaultAttributesPublic,
789
+ order: [
790
+ ["counter_users", "desc"],
791
+ [
792
+ { model: models.Image, as: "GroupLogoImages" },
793
+ "created_at",
794
+ "asc",
795
+ ],
796
+ [
797
+ { model: models.Image, as: "GroupHeaderImages" },
798
+ "created_at",
799
+ "asc",
800
+ ],
801
+ [{ model: models.Category }, "name", "asc"],
802
+ ],
803
+ include: [
804
+ {
805
+ model: models.User,
806
+ as: "GroupUsers",
807
+ attributes: ["id"],
808
+ required: true,
809
+ where: {
810
+ id: req.user.id,
811
+ },
812
+ },
813
+ ].concat(models.Group.masterGroupIncludes(models)),
814
+ })
815
+ .then(function (groups) {
816
+ userGroups = groups;
817
+ parallelCallback(null, "users");
818
+ })
819
+ .catch(function (error) {
820
+ parallelCallback(error);
821
+ });
822
+ },
823
+ ], function (error) {
824
+ if (error) {
825
+ seriesCallback(error);
826
+ }
827
+ else {
828
+ var combinedGroups = _.concat(userGroups, groupFolder.dataValues.Groups);
829
+ if (adminGroups) {
830
+ combinedGroups = _.concat(adminGroups, combinedGroups);
831
+ }
832
+ combinedGroups = _.uniqBy(combinedGroups, function (group) {
833
+ if (!group) {
834
+ log.error("Can't find group in combinedGroups", {
835
+ combinedGroupsL: combinedGroups.length,
836
+ err: "Cant find group in combinedGroups",
837
+ });
838
+ return null;
839
+ }
840
+ else {
841
+ return group.id;
842
+ }
843
+ });
844
+ models.Group.addVideosAndCommunityLinksToGroups(combinedGroups, (videoError) => {
845
+ groupFolder.dataValues.Groups = combinedGroups;
846
+ seriesCallback(videoError);
847
+ });
848
+ }
849
+ });
850
+ }
851
+ else {
852
+ models.Group.addVideosAndCommunityLinksToGroups(groupFolder.dataValues.Groups, (videoError) => {
853
+ seriesCallback(videoError);
854
+ });
855
+ }
856
+ },
857
+ ], function (error) {
858
+ done(error, groupFolder);
859
+ });
860
+ };
861
+ router.post("/:id/getPresignedAttachmentURL", auth.can("add to group"), function (req, res) {
862
+ const endPoint = process.env.S3_ENDPOINT || "s3.amazonaws.com";
863
+ const accelEndPoint = process.env.S3_ACCELERATED_ENDPOINT ||
864
+ process.env.S3_ENDPOINT ||
865
+ "s3.amazonaws.com";
866
+ const s3 = new aws.S3({
867
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
868
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
869
+ endpoint: accelEndPoint,
870
+ signatureVersion: "v4",
871
+ useAccelerateEndpoint: process.env.S3_ACCELERATED_ENDPOINT != null,
872
+ region: process.env.S3_REGION ? process.env.S3_REGION : "eu-west-1",
873
+ s3ForcePathStyle: process.env.S3_FORCE_PATH_STYLE ? true : false,
874
+ });
875
+ const signedUrlExpireSeconds = 60 * 60;
876
+ const bucketName = process.env.S3_ATTACHMENTS_BUCKET;
877
+ // const contentType = req.body.contentType ? req.body.contentType : 'application/octet-stream';
878
+ const contentType = req.body.contentType
879
+ ? req.body.contentType
880
+ : "application/octet-stream";
881
+ const randomCode = Math.random().toString(36).substring(2, 9);
882
+ const fileKey = randomCode + "/" + req.body.filename;
883
+ const s3Params = {
884
+ Bucket: bucketName,
885
+ Key: fileKey,
886
+ Expires: signedUrlExpireSeconds,
887
+ ACL: process.env.S3_FORCE_PATH_STYLE ? undefined : "public-read",
888
+ ContentType: contentType,
889
+ };
890
+ s3.getSignedUrl("putObject", s3Params, (error, url) => {
891
+ if (error) {
892
+ log.error("Error getting presigned attachment url from AWS S3", {
893
+ error,
894
+ });
895
+ res.sendStatus(500);
896
+ }
897
+ else {
898
+ log.info("Presigned URL:", { url });
899
+ res.send({ presignedUrl: url });
900
+ }
901
+ });
902
+ });
903
+ router.delete("/:groupId/:activityId/delete_activity", auth.can("edit group"), function (req, res) {
904
+ models.AcActivity.findOne({
905
+ where: {
906
+ group_id: req.params.groupId,
907
+ id: req.params.activityId,
908
+ },
909
+ })
910
+ .then(function (activity) {
911
+ activity.deleted = true;
912
+ activity.save().then(function () {
913
+ res.send({ activityId: activity.id });
914
+ });
915
+ })
916
+ .catch(function (error) {
917
+ log.error("Could not delete activity for group", {
918
+ err: error,
919
+ context: "delete_activity",
920
+ user: toJson(req.user.simple()),
921
+ });
922
+ res.sendStatus(500);
923
+ });
924
+ });
925
+ router.delete("/:groupId/user_membership", auth.isLoggedInNoAnonymousCheck, auth.can("view group"), function (req, res) {
926
+ getGroupAndUser(req.params.groupId, req.user.id, null, function (error, group, user) {
927
+ if (error) {
928
+ log.error("Could not remove user", {
929
+ err: error,
930
+ groupId: req.params.groupId,
931
+ userRemovedId: req.user.id,
932
+ context: "user_membership",
933
+ user: toJson(req.user.simple()),
934
+ });
935
+ res.sendStatus(500);
936
+ }
937
+ else if (user && group) {
938
+ group.removeGroupUsers(user).then(function (results) {
939
+ log.info("User removed", {
940
+ context: "remove_admin",
941
+ groupId: req.params.groupId,
942
+ userRemovedId: req.user.id,
943
+ user: toJson(req.user.simple()),
944
+ });
945
+ res.send({ membershipValue: false, name: group.name });
946
+ });
947
+ }
948
+ else {
949
+ res.sendStatus(404);
950
+ }
951
+ });
952
+ });
953
+ router.post("/:groupId/user_membership", auth.isLoggedInNoAnonymousCheck, auth.can("add to group"), function (req, res) {
954
+ getGroupAndUser(req.params.groupId, req.user.id, null, function (error, group, user) {
955
+ if (error) {
956
+ log.error("Could not add user", {
957
+ err: error,
958
+ groupId: req.params.groupId,
959
+ userRemovedId: req.user.id,
960
+ context: "user_membership",
961
+ user: toJson(req.user.simple()),
962
+ });
963
+ res.sendStatus(500);
964
+ }
965
+ else if (user && group) {
966
+ group.addGroupUsers(user).then(function (results) {
967
+ log.info("User Added", {
968
+ context: "user_membership",
969
+ groupId: req.params.groupId,
970
+ userRemovedId: req.user.id,
971
+ user: toJson(req.user.simple()),
972
+ });
973
+ res.send({ membershipValue: true, name: group.name });
974
+ });
975
+ }
976
+ else {
977
+ res.sendStatus(404);
978
+ }
979
+ });
980
+ });
981
+ router.post("/:groupId/sendEmailInvitesForAnons", auth.can("edit group"), async function (req, res) {
982
+ try {
983
+ const group = await models.Group.findOne({
984
+ where: { id: req.params.groupId },
985
+ attributes: ["id", "community_id"],
986
+ });
987
+ const emails = req.body.emails;
988
+ if (!emails) {
989
+ res.sendStatus(400);
990
+ log.error("No emails provided", {
991
+ emails,
992
+ });
993
+ return;
994
+ }
995
+ const emailArray = emails
996
+ .split("\n")
997
+ .map((email) => email.trim());
998
+ // Validate each email
999
+ const validEmails = emailArray.filter((email) => {
1000
+ // Basic email validation regex
1001
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1002
+ return emailRegex.test(email);
1003
+ });
1004
+ if (validEmails.length !== emailArray.length) {
1005
+ log.error("Invalid email addresses", {
1006
+ invalidEmails: emailArray.filter((email) => !validEmails.includes(email)),
1007
+ });
1008
+ }
1009
+ const { AgentInviteManager } = await import("../agents/managers/emailInvitesManager.js");
1010
+ for (const email of validEmails) {
1011
+ const token = crypto.randomBytes(20).toString("hex");
1012
+ const invite = await models.Invite.create({
1013
+ token,
1014
+ expires_at: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year expiry
1015
+ type: models.Invite.INVITE_TO_COMMUNITY_AND_GROUP_AS_ANON,
1016
+ community_id: group.community_id,
1017
+ from_user_id: req.user.id,
1018
+ });
1019
+ const invite_link = `https://app.${req.ypDomain.domain_name}/group/${group.id}?anonInvite=1&token=${token}&forAgentBundle=1`;
1020
+ await AgentInviteManager.sendInviteEmail(invite_link, req.body.agentRunId, group.id, req.user, email);
1021
+ log.info("Invite Created", {
1022
+ email,
1023
+ inviteId: invite.id,
1024
+ invite_link,
1025
+ });
1026
+ }
1027
+ res.sendStatus(200);
1028
+ }
1029
+ catch (error) {
1030
+ log.error("Error inviting user emails as anons", {
1031
+ error,
1032
+ });
1033
+ res.sendStatus(500);
1034
+ }
1035
+ });
1036
+ router.post("/:groupId/:userEmail/invite_user", auth.can("edit group"), function (req, res) {
1037
+ var invite, user, token;
1038
+ async.series([
1039
+ function (callback) {
1040
+ crypto.randomBytes(20, function (error, buf) {
1041
+ token = buf.toString("hex");
1042
+ callback(error);
1043
+ });
1044
+ },
1045
+ function (callback) {
1046
+ models.User.findOne({
1047
+ where: { email: req.params.userEmail },
1048
+ attributes: ["id", "email"],
1049
+ })
1050
+ .then(function (userIn) {
1051
+ if (userIn) {
1052
+ user = userIn;
1053
+ }
1054
+ callback();
1055
+ })
1056
+ .catch(function (error) {
1057
+ callback(error);
1058
+ });
1059
+ },
1060
+ function (callback) {
1061
+ if (!req.query.addToGroupDirectly) {
1062
+ models.Invite.create({
1063
+ token: token,
1064
+ expires_at: Date.now() + 3600000 * 24 * 30 * 365 * 1000,
1065
+ type: models.Invite.INVITE_TO_GROUP,
1066
+ group_id: req.params.groupId,
1067
+ domain_id: req.ypDomain.id,
1068
+ user_id: user ? user.id : null,
1069
+ from_user_id: req.user.id,
1070
+ metadata: { toEmail: req.params.userEmail },
1071
+ })
1072
+ .then(function (inviteIn) {
1073
+ if (inviteIn) {
1074
+ invite = inviteIn;
1075
+ callback();
1076
+ }
1077
+ else {
1078
+ callback("Invite not found");
1079
+ }
1080
+ })
1081
+ .catch(function (error) {
1082
+ callback(error);
1083
+ });
1084
+ }
1085
+ else {
1086
+ callback();
1087
+ }
1088
+ },
1089
+ function (callback) {
1090
+ if (!req.query.addToGroupDirectly) {
1091
+ models.AcActivity.inviteCreated({
1092
+ email: req.params.userEmail,
1093
+ user_id: user ? user.id : null,
1094
+ sender_user_id: req.user.id,
1095
+ sender_name: req.user.name,
1096
+ group_id: req.params.groupId,
1097
+ domain_id: req.ypDomain.id,
1098
+ invite_id: invite.id,
1099
+ token: token,
1100
+ }, function (error) {
1101
+ callback(error);
1102
+ });
1103
+ }
1104
+ else {
1105
+ callback();
1106
+ }
1107
+ },
1108
+ function (callback) {
1109
+ if (user && req.query.addToGroupDirectly) {
1110
+ models.Group.findOne({
1111
+ where: {
1112
+ id: req.params.groupId,
1113
+ },
1114
+ attributes: ["id"],
1115
+ })
1116
+ .then((group) => {
1117
+ if (group) {
1118
+ group
1119
+ .addGroupUsers(user)
1120
+ .then(() => {
1121
+ callback();
1122
+ })
1123
+ .catch((error) => {
1124
+ callback(error);
1125
+ });
1126
+ }
1127
+ else {
1128
+ callback("Can't find group");
1129
+ }
1130
+ })
1131
+ .catch((error) => {
1132
+ callback(error);
1133
+ });
1134
+ }
1135
+ else {
1136
+ callback();
1137
+ }
1138
+ },
1139
+ ], function (error) {
1140
+ if (error) {
1141
+ log.error("Send Invite Error", {
1142
+ user: user ? toJson(user) : null,
1143
+ context: "invite_user",
1144
+ loggedInUser: toJson(req.user),
1145
+ err: error,
1146
+ errorStatus: 500,
1147
+ });
1148
+ res.sendStatus(500);
1149
+ }
1150
+ else {
1151
+ if (!user && req.query.addToGroupDirectly) {
1152
+ log.info("Send Invite User Not Found To add", {
1153
+ userEmail: req.params.userEmail,
1154
+ user: null,
1155
+ context: "invite_user_community",
1156
+ loggedInUser: toJson(req.user),
1157
+ });
1158
+ res.sendStatus(404);
1159
+ }
1160
+ else {
1161
+ log.info("Send Invite Activity Created", {
1162
+ userEmail: req.params.userEmail,
1163
+ user: user ? toJson(user) : null,
1164
+ context: "invite_user",
1165
+ loggedInUser: toJson(req.user),
1166
+ });
1167
+ res.sendStatus(200);
1168
+ }
1169
+ }
1170
+ });
1171
+ });
1172
+ router.post("/:groupId/add_page", auth.can("edit group"), function (req, res) {
1173
+ models.Page.newPage(req, { group_id: req.params.groupId, content: {}, title: {} }, function (error, pages) {
1174
+ if (error) {
1175
+ log.error("Could not create page for admin for group", {
1176
+ err: error,
1177
+ context: "new_page",
1178
+ user: toJson(req.user.simple()),
1179
+ });
1180
+ res.sendStatus(500);
1181
+ }
1182
+ else {
1183
+ log.info("New Community Page", {
1184
+ context: "new_page",
1185
+ user: toJson(req.user.simple()),
1186
+ });
1187
+ res.sendStatus(200);
1188
+ }
1189
+ });
1190
+ });
1191
+ router.post("/:domainId/create_community_for_group", auth.can("create community"), function (req, res) {
1192
+ log.info("Creating Community for Group", {
1193
+ context: "create",
1194
+ user: toJson(req.user),
1195
+ });
1196
+ var admin_email = req.user.email;
1197
+ var admin_name = "Administrator";
1198
+ var community = models.Community.build({
1199
+ name: "Community for Group: " + req.body.name,
1200
+ description: "Community for Group",
1201
+ access: 0,
1202
+ status: "hidden",
1203
+ domain_id: req.params.domainId,
1204
+ user_id: req.user.id,
1205
+ admin_email: admin_email,
1206
+ admin_name: admin_name,
1207
+ configuration: {
1208
+ theme: {
1209
+ oneColorScheme: "tonal",
1210
+ variant: "monochrome",
1211
+ },
1212
+ onlyAdminsCanCreateGroups: true,
1213
+ },
1214
+ hostname: req.body.hostname || `${uuidv4()}.${req.ypDomain.domain_name}`,
1215
+ user_agent: req.useragent.source,
1216
+ ip_address: req.clientIp,
1217
+ });
1218
+ community
1219
+ .save()
1220
+ .then(function (newCommunity) {
1221
+ log.info("Community Created", {
1222
+ domainId: newCommunity.domain_id,
1223
+ context: "create",
1224
+ user: toJson(req.user),
1225
+ });
1226
+ req.params.communityId = newCommunity.id;
1227
+ newCommunity.updateAllExternalCounters(req, "up", "counter_communities", function () {
1228
+ newCommunity.addCommunityAdmins(req.user).then(function (results) {
1229
+ createGroup(req, res);
1230
+ });
1231
+ });
1232
+ })
1233
+ .catch(function (error) {
1234
+ log.error("Could not create community for group", {
1235
+ err: error,
1236
+ context: "create",
1237
+ user: toJson(req.user),
1238
+ });
1239
+ res.sendStatus(500);
1240
+ });
1241
+ });
1242
+ router.post("/:communityId", auth.can("create group"), function (req, res) {
1243
+ createGroup(req, res);
1244
+ });
1245
+ router.delete("/:groupId/:userId/remove_admin", auth.can("edit group"), function (req, res) {
1246
+ getGroupAndUser(req.params.groupId, req.params.userId, null, function (error, group, user) {
1247
+ if (error) {
1248
+ log.error("Could not remove admin", {
1249
+ err: error,
1250
+ groupId: req.params.groupId,
1251
+ userRemovedId: req.params.userId,
1252
+ context: "remove_admin",
1253
+ user: toJson(req.user.simple()),
1254
+ });
1255
+ res.sendStatus(500);
1256
+ }
1257
+ else if (user && group) {
1258
+ group.removeGroupAdmins(user).then(function (results) {
1259
+ log.info("Admin removed", {
1260
+ context: "remove_admin",
1261
+ groupId: req.params.groupId,
1262
+ userRemovedId: req.params.userId,
1263
+ user: toJson(req.user.simple()),
1264
+ });
1265
+ res.sendStatus(200);
1266
+ });
1267
+ }
1268
+ else {
1269
+ res.sendStatus(404);
1270
+ }
1271
+ });
1272
+ });
1273
+ router.delete("/:groupId/:userId/remove_promoter", auth.can("edit group"), function (req, res) {
1274
+ getGroupAndUser(req.params.groupId, req.params.userId, null, function (error, group, user) {
1275
+ if (error) {
1276
+ log.error("Could not remove promoter", {
1277
+ err: error,
1278
+ groupId: req.params.groupId,
1279
+ userRemovedId: req.params.userId,
1280
+ context: "remove_promoter",
1281
+ user: toJson(req.user.simple()),
1282
+ });
1283
+ res.sendStatus(500);
1284
+ }
1285
+ else if (user && group) {
1286
+ group.removeGroupPromoters(user).then(function (results) {
1287
+ log.info("Promoter removed", {
1288
+ context: "remove_promoter",
1289
+ groupId: req.params.groupId,
1290
+ userRemovedId: req.params.userId,
1291
+ user: toJson(req.user.simple()),
1292
+ });
1293
+ res.sendStatus(200);
1294
+ });
1295
+ }
1296
+ else {
1297
+ res.sendStatus(404);
1298
+ }
1299
+ });
1300
+ });
1301
+ router.get("/:groupId/has_promotion_access", auth.can("view group"), async (req, res) => {
1302
+ if (req.user) {
1303
+ if (await req.user.hasPromotionAccess(req.params.groupId)) {
1304
+ res.sendStatus(200);
1305
+ }
1306
+ else {
1307
+ res.sendStatus(401);
1308
+ }
1309
+ }
1310
+ else {
1311
+ res.sendStatus(401);
1312
+ }
1313
+ });
1314
+ router.delete("/:groupId/remove_many_admins", auth.can("edit group"), function (req, res) {
1315
+ queue.add("process-deletion", {
1316
+ type: "remove-many-group-admins",
1317
+ userIds: req.body.userIds,
1318
+ groupId: req.params.groupId,
1319
+ }, "high");
1320
+ log.info("Remove many admins started", {
1321
+ context: "remove_many_admins",
1322
+ groupId: req.params.groupId,
1323
+ user: toJson(req.user.simple()),
1324
+ });
1325
+ res.sendStatus(200);
1326
+ });
1327
+ router.delete("/:groupId/remove_many_promoters", auth.can("edit group"), function (req, res) {
1328
+ queue.add("process-deletion", {
1329
+ type: "remove-many-group-promoters",
1330
+ userIds: req.body.userIds,
1331
+ groupId: req.params.groupId,
1332
+ }, "high");
1333
+ log.info("Remove many promoters started", {
1334
+ context: "remove_many_promoters",
1335
+ groupId: req.params.groupId,
1336
+ user: toJson(req.user.simple()),
1337
+ });
1338
+ res.sendStatus(200);
1339
+ });
1340
+ router.delete("/:groupId/remove_many_users_and_delete_content", auth.can("edit group"), function (req, res) {
1341
+ queue.add("process-deletion", {
1342
+ type: "remove-many-group-users-and-delete-content",
1343
+ userIds: req.body.userIds,
1344
+ groupId: req.params.groupId,
1345
+ }, "high");
1346
+ log.info("Remove many and delete many users content", {
1347
+ context: "remove_many_users_and_delete_content",
1348
+ groupId: req.params.groupId,
1349
+ user: toJson(req.user.simple()),
1350
+ });
1351
+ res.sendStatus(200);
1352
+ });
1353
+ router.delete("/:groupId/remove_many_users", auth.can("edit group"), function (req, res) {
1354
+ queue.add("process-deletion", {
1355
+ type: "remove-many-group-users",
1356
+ userIds: req.body.userIds,
1357
+ groupId: req.params.groupId,
1358
+ }, "high");
1359
+ log.info("Remove many admins started", {
1360
+ context: "remove_many_users",
1361
+ groupId: req.params.groupId,
1362
+ user: toJson(req.user.simple()),
1363
+ });
1364
+ res.sendStatus(200);
1365
+ });
1366
+ router.delete("/:groupId/:userId/remove_and_delete_user_content", auth.can("edit group"), function (req, res) {
1367
+ getGroupAndUser(req.params.groupId, req.params.userId, null, function (error, group, user) {
1368
+ if (error) {
1369
+ log.error("Could not remove_user", {
1370
+ err: error,
1371
+ groupId: req.params.groupId,
1372
+ userRemovedId: req.params.userId,
1373
+ context: "remove_user",
1374
+ user: toJson(req.user.simple()),
1375
+ });
1376
+ res.sendStatus(500);
1377
+ }
1378
+ else if (user && group) {
1379
+ group.removeGroupUsers(user).then(function (results) {
1380
+ if (group.counter_users > 0) {
1381
+ group.decrement("counter_users");
1382
+ }
1383
+ queue.add("process-deletion", {
1384
+ type: "delete-group-user-content",
1385
+ userId: req.params.userId,
1386
+ groupId: req.params.groupId,
1387
+ }, "high");
1388
+ log.info("User removed", {
1389
+ context: "remove_and_delete_user_content",
1390
+ groupId: req.params.groupId,
1391
+ userRemovedId: req.params.userId,
1392
+ user: toJson(req.user.simple()),
1393
+ });
1394
+ res.sendStatus(200);
1395
+ });
1396
+ }
1397
+ else {
1398
+ res.sendStatus(404);
1399
+ }
1400
+ });
1401
+ });
1402
+ router.delete("/:groupId/:userId/remove_user", auth.can("edit group"), function (req, res) {
1403
+ getGroupAndUser(req.params.groupId, req.params.userId, null, function (error, group, user) {
1404
+ if (error) {
1405
+ log.error("Could not remove_user", {
1406
+ err: error,
1407
+ groupId: req.params.groupId,
1408
+ userRemovedId: req.params.userId,
1409
+ context: "remove_user",
1410
+ user: toJson(req.user.simple()),
1411
+ });
1412
+ res.sendStatus(500);
1413
+ }
1414
+ else if (user && group) {
1415
+ group.removeGroupUsers(user).then(function (results) {
1416
+ if (group.counter_users > 0) {
1417
+ group.decrement("counter_users");
1418
+ }
1419
+ log.info("User removed", {
1420
+ context: "remove_user",
1421
+ groupId: req.params.groupId,
1422
+ userRemovedId: req.params.userId,
1423
+ user: toJson(req.user.simple()),
1424
+ });
1425
+ res.sendStatus(200);
1426
+ });
1427
+ }
1428
+ else {
1429
+ res.sendStatus(404);
1430
+ }
1431
+ });
1432
+ });
1433
+ router.post("/:groupId/:email/add_admin", auth.can("edit group"), function (req, res) {
1434
+ getGroupAndUser(req.params.groupId, null, req.params.email, function (error, group, user) {
1435
+ if (error) {
1436
+ log.error("Could not add admin", {
1437
+ err: error,
1438
+ groupId: req.params.groupId,
1439
+ userAddEmail: req.params.email,
1440
+ context: "add_admin",
1441
+ user: toJson(req.user.simple()),
1442
+ });
1443
+ res.sendStatus(500);
1444
+ }
1445
+ else if (user && group) {
1446
+ group.addGroupAdmins(user).then(function (results) {
1447
+ log.info("Admin Added", {
1448
+ context: "add_admin",
1449
+ groupId: req.params.groupId,
1450
+ userAddEmail: req.params.email,
1451
+ user: toJson(req.user.simple()),
1452
+ });
1453
+ res.sendStatus(200);
1454
+ });
1455
+ }
1456
+ else {
1457
+ res.sendStatus(404);
1458
+ }
1459
+ });
1460
+ });
1461
+ router.post("/:groupId/:email/add_promoter", auth.can("edit group"), function (req, res) {
1462
+ getGroupAndUser(req.params.groupId, null, req.params.email, function (error, group, user) {
1463
+ if (error) {
1464
+ log.error("Could not add promoter", {
1465
+ err: error,
1466
+ groupId: req.params.groupId,
1467
+ userAddEmail: req.params.email,
1468
+ context: "add_promoter",
1469
+ user: toJson(req.user.simple()),
1470
+ });
1471
+ res.sendStatus(500);
1472
+ }
1473
+ else if (user && group) {
1474
+ group.addGroupPromoters(user).then(function (results) {
1475
+ log.info("Promoter Added", {
1476
+ context: "add_promoter",
1477
+ groupId: req.params.groupId,
1478
+ userAddEmail: req.params.email,
1479
+ user: toJson(req.user.simple()),
1480
+ });
1481
+ res.sendStatus(200);
1482
+ });
1483
+ }
1484
+ else {
1485
+ res.sendStatus(404);
1486
+ }
1487
+ });
1488
+ });
1489
+ router.get("/:groupId/pages", auth.can("view group"), function (req, res) {
1490
+ const redisKey = "cache:groupPages:" + req.params.groupId;
1491
+ req.redisClient
1492
+ .get(redisKey)
1493
+ .then((pages) => {
1494
+ if (pages) {
1495
+ res.send(JSON.parse(pages));
1496
+ }
1497
+ else {
1498
+ models.Group.findOne({
1499
+ where: { id: req.params.groupId },
1500
+ attributes: ["id"],
1501
+ include: [
1502
+ {
1503
+ model: models.Community,
1504
+ attributes: ["id"],
1505
+ include: [
1506
+ {
1507
+ model: models.Domain,
1508
+ attributes: ["id"],
1509
+ },
1510
+ ],
1511
+ },
1512
+ ],
1513
+ })
1514
+ .then(function (group) {
1515
+ models.Page.getPages(req, {
1516
+ group_id: req.params.groupId,
1517
+ community_id: group.Community.id,
1518
+ domain_id: group.Community.Domain.id,
1519
+ }, function (error, pages) {
1520
+ if (error) {
1521
+ log.error("Could not get pages for group", {
1522
+ err: error,
1523
+ context: "pages",
1524
+ user: req.user ? toJson(req.user.simple()) : null,
1525
+ });
1526
+ res.sendStatus(500);
1527
+ }
1528
+ else {
1529
+ log.info("Got Pages", {
1530
+ userId: req.user ? req.user.id : null,
1531
+ });
1532
+ req.redisClient.setEx(redisKey, process.env.PAGES_CACHE_TTL
1533
+ ? parseInt(process.env.PAGES_CACHE_TTL)
1534
+ : 3, JSON.stringify(pages));
1535
+ res.send(pages);
1536
+ }
1537
+ });
1538
+ return null;
1539
+ })
1540
+ .catch(function (error) {
1541
+ log.error("Could not get pages for group", {
1542
+ err: error,
1543
+ context: "pages",
1544
+ user: req.user ? toJson(req.user.simple()) : null,
1545
+ });
1546
+ res.sendStatus(500);
1547
+ });
1548
+ }
1549
+ })
1550
+ .catch((error) => {
1551
+ log.error("Could not get pages for group from redis", {
1552
+ err: error,
1553
+ context: "pages",
1554
+ userId: req.user ? req.user.id : null,
1555
+ });
1556
+ res.sendStatus(500);
1557
+ });
1558
+ });
1559
+ router.get("/:groupId/pages_for_admin", auth.can("edit group"), function (req, res) {
1560
+ models.Page.getPagesForAdmin(req, { group_id: req.params.groupId }, function (error, pages) {
1561
+ if (error) {
1562
+ log.error("Could not get page for admin for group", {
1563
+ err: error,
1564
+ context: "pages_for_admin",
1565
+ user: toJson(req.user.simple()),
1566
+ });
1567
+ res.sendStatus(500);
1568
+ }
1569
+ else {
1570
+ log.info("Got Pages For Admin", {
1571
+ context: "pages_for_admin",
1572
+ userId: req.user ? req.user.id : null,
1573
+ });
1574
+ res.send(pages);
1575
+ }
1576
+ });
1577
+ });
1578
+ router.put("/:groupId/:type/start_report_creation", auth.can("edit group"), function (req, res) {
1579
+ models.AcBackgroundJob.createJob({}, {}, (error, jobId) => {
1580
+ if (error) {
1581
+ log.error("Could not create backgroundJob", {
1582
+ err: error,
1583
+ context: "start_report_creation",
1584
+ user: toJson(req.user.simple()),
1585
+ });
1586
+ res.sendStatus(500);
1587
+ }
1588
+ else {
1589
+ let reportType;
1590
+ if (req.params.type === "docx") {
1591
+ reportType = "start-docx-report-generation";
1592
+ }
1593
+ else if (req.params.type === "xls") {
1594
+ reportType = "start-xls-report-generation";
1595
+ }
1596
+ queue.add("process-reports", {
1597
+ type: reportType,
1598
+ userId: req.user.id,
1599
+ exportType: req.params.type,
1600
+ translateLanguage: req.query.translateLanguage,
1601
+ jobId: jobId,
1602
+ groupId: req.params.groupId,
1603
+ }, "critical");
1604
+ res.send({ jobId });
1605
+ }
1606
+ });
1607
+ });
1608
+ router.get("/:groupId/:jobId/report_creation_progress", auth.can("edit group"), function (req, res) {
1609
+ models.AcBackgroundJob.findOne({
1610
+ where: {
1611
+ id: req.params.jobId,
1612
+ },
1613
+ attributes: ["id", "progress", "error", "data"],
1614
+ })
1615
+ .then((job) => {
1616
+ res.send(job);
1617
+ })
1618
+ .catch((error) => {
1619
+ log.error("Could not get backgroundJob", {
1620
+ err: error,
1621
+ context: "start_report_creation",
1622
+ user: toJson(req.user.simple()),
1623
+ });
1624
+ res.sendStatus(500);
1625
+ });
1626
+ });
1627
+ //TODO: Fix this permission back to edit
1628
+ router.post("/:groupId/:start_generating/ai_image", auth.can("view group"), function (req, res) {
1629
+ models.AcBackgroundJob.createJob({}, {}, (error, jobId) => {
1630
+ if (error) {
1631
+ log.error("Could not create backgroundJob", {
1632
+ err: error,
1633
+ context: "start_generating_ai_image",
1634
+ user: req.user ? toJson(req.user.simple()) : null,
1635
+ });
1636
+ res.sendStatus(500);
1637
+ }
1638
+ else {
1639
+ queue.add("process-generative-ai", {
1640
+ type: "collection-image",
1641
+ //TODO: Look into this
1642
+ userId: req.user ? req.user.id : 1,
1643
+ jobId: jobId,
1644
+ collectionId: req.params.groupId,
1645
+ collectionType: "group",
1646
+ prompt: req.body.prompt,
1647
+ imageType: req.body.imageType,
1648
+ }, "critical");
1649
+ res.send({ jobId });
1650
+ }
1651
+ });
1652
+ });
1653
+ //TODO: Fix this permission back to edit
1654
+ router.get("/:groupId/:jobId/poll_for_generating_ai_image", auth.can("view group"), function (req, res) {
1655
+ models.AcBackgroundJob.findOne({
1656
+ where: {
1657
+ id: req.params.jobId,
1658
+ },
1659
+ attributes: ["id", "progress", "error", "data"],
1660
+ })
1661
+ .then((job) => {
1662
+ res.send(job);
1663
+ })
1664
+ .catch((error) => {
1665
+ log.error("Could not get backgroundJob", {
1666
+ err: error,
1667
+ context: "poll_for_generating_ai_image",
1668
+ user: req.user ? toJson(req.user.simple()) : null,
1669
+ });
1670
+ res.sendStatus(500);
1671
+ });
1672
+ });
1673
+ router.get("/:groupId/export_group", auth.can("edit group"), function (req, res) {
1674
+ models.Group.findOne({
1675
+ where: {
1676
+ id: req.params.groupId,
1677
+ },
1678
+ attributes: ["id", "name", "community_id", "configuration"],
1679
+ })
1680
+ .then(function (group) {
1681
+ if (group) {
1682
+ getExportFileDataForGroup(group, req.ypDomain.domain_name, function (error, fileData) {
1683
+ if (error) {
1684
+ log.error("Could not export for group", {
1685
+ err: error,
1686
+ context: "export_group",
1687
+ user: toJson(req.user.simple()),
1688
+ });
1689
+ res.sendStatus(500);
1690
+ }
1691
+ else {
1692
+ log.info("Got Export Admin", {
1693
+ context: "export_group",
1694
+ user: toJson(req.user.simple()),
1695
+ });
1696
+ var groupName = sanitizeFilename(group.name).replace(/ /g, "");
1697
+ var dateString = moment(new Date()).format("DD_MM_YY_HH_mm");
1698
+ var filename = "ideas_and_points_group_export_" +
1699
+ group.community_id +
1700
+ "_" +
1701
+ req.params.groupId +
1702
+ "_" +
1703
+ groupName +
1704
+ "_" +
1705
+ dateString +
1706
+ ".csv";
1707
+ res.set({
1708
+ "content-type": "application/octet-stream; charset=utf-8",
1709
+ });
1710
+ res.charset = "utf-8";
1711
+ res.attachment(filename);
1712
+ res.send(fileData);
1713
+ }
1714
+ });
1715
+ }
1716
+ else {
1717
+ log.error("Cant find group", {
1718
+ err: error,
1719
+ context: "export_group",
1720
+ user: toJson(req.user.simple()),
1721
+ });
1722
+ res.sendStatus(404);
1723
+ }
1724
+ })
1725
+ .catch(function (error) {
1726
+ log.error("Could not export for group", {
1727
+ err: error,
1728
+ context: "export_group",
1729
+ user: toJson(req.user.simple()),
1730
+ });
1731
+ res.sendStatus(500);
1732
+ });
1733
+ });
1734
+ router.get("/:groupId/export_group_docx", auth.can("edit group"), async (req, res) => {
1735
+ models.Group.findOne({
1736
+ where: {
1737
+ id: req.params.groupId,
1738
+ },
1739
+ attributes: [
1740
+ "id",
1741
+ "name",
1742
+ "community_id",
1743
+ "objectives",
1744
+ "configuration",
1745
+ "language",
1746
+ ],
1747
+ })
1748
+ .then(function (group) {
1749
+ if (group) {
1750
+ exportGroupToDocx(group, req.ypDomain.domain_name, req.query.translateLanguage, function (error, fileData) {
1751
+ if (error) {
1752
+ log.error("Could not export for group", {
1753
+ err: error,
1754
+ context: "export_group",
1755
+ user: toJson(req.user.simple()),
1756
+ });
1757
+ res.sendStatus(500);
1758
+ }
1759
+ else {
1760
+ log.info("Got Export Admin", {
1761
+ context: "export_group",
1762
+ user: toJson(req.user.simple()),
1763
+ });
1764
+ var groupName = sanitizeFilename(group.name).replace(/ /g, "");
1765
+ var dateString = moment(new Date()).format("DD_MM_YY_HH_mm");
1766
+ var filename = "ideas_and_points_group_export_" +
1767
+ group.community_id +
1768
+ "_" +
1769
+ req.params.groupId +
1770
+ "_" +
1771
+ groupName +
1772
+ "_" +
1773
+ dateString +
1774
+ ".docx";
1775
+ res.set({
1776
+ "content-type": "application/application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=utf-8",
1777
+ });
1778
+ res.setHeader("Content-Disposition", "attachment; filename=" + filename);
1779
+ res.charset = "utf-8";
1780
+ res.attachment(filename);
1781
+ res.send(fileData);
1782
+ }
1783
+ });
1784
+ }
1785
+ else {
1786
+ log.error("Cant find group", {
1787
+ err: error,
1788
+ context: "export_group",
1789
+ user: toJson(req.user.simple()),
1790
+ });
1791
+ res.sendStatus(404);
1792
+ }
1793
+ })
1794
+ .catch(function (error) {
1795
+ log.error("Could not export for group", {
1796
+ err: error,
1797
+ context: "export_group",
1798
+ user: toJson(req.user.simple()),
1799
+ });
1800
+ res.sendStatus(500);
1801
+ });
1802
+ });
1803
+ router.put("/:groupId/:pageId/update_page_locale", auth.can("edit group"), function (req, res) {
1804
+ models.Page.updatePageLocale(req, { group_id: req.params.groupId, id: req.params.pageId }, function (error) {
1805
+ if (error) {
1806
+ log.error("Could not update locale for admin for group", {
1807
+ err: error,
1808
+ context: "update_page_locale",
1809
+ user: toJson(req.user.simple()),
1810
+ });
1811
+ res.sendStatus(500);
1812
+ }
1813
+ else {
1814
+ log.info("Community Page Locale Updated", {
1815
+ context: "update_page_locale",
1816
+ user: toJson(req.user.simple()),
1817
+ });
1818
+ res.sendStatus(200);
1819
+ }
1820
+ });
1821
+ });
1822
+ router.put("/:groupId/:pageId/update_page_weight", auth.can("edit group"), function (req, res) {
1823
+ models.Page.updatePageWeight(req, { group_id: req.params.groupId, id: req.params.pageId }, function (error) {
1824
+ if (error) {
1825
+ log.error("Could not update locale for admin for group", {
1826
+ err: error,
1827
+ context: "update_page_locale",
1828
+ user: toJson(req.user.simple()),
1829
+ });
1830
+ res.sendStatus(500);
1831
+ }
1832
+ else {
1833
+ log.info("Community Page Locale Updated", {
1834
+ context: "update_page_locale",
1835
+ user: toJson(req.user.simple()),
1836
+ });
1837
+ res.sendStatus(200);
1838
+ }
1839
+ });
1840
+ });
1841
+ router.put("/:groupId/:pageId/publish_page", auth.can("edit group"), function (req, res) {
1842
+ models.Page.publishPage(req, { group_id: req.params.groupId, id: req.params.pageId }, function (error) {
1843
+ if (error) {
1844
+ log.error("Could not publish page for admin for group", {
1845
+ err: error,
1846
+ context: "publish_page",
1847
+ user: toJson(req.user.simple()),
1848
+ });
1849
+ res.sendStatus(500);
1850
+ }
1851
+ else {
1852
+ log.info("Community Page Published", {
1853
+ context: "publish_page",
1854
+ user: toJson(req.user.simple()),
1855
+ });
1856
+ res.sendStatus(200);
1857
+ }
1858
+ });
1859
+ });
1860
+ router.put("/:groupId/:pageId/un_publish_page", auth.can("edit group"), function (req, res) {
1861
+ models.Page.unPublishPage(req, { group_id: req.params.groupId, id: req.params.pageId }, function (error) {
1862
+ if (error) {
1863
+ log.error("Could not un-publish page for admin for group", {
1864
+ err: error,
1865
+ context: "un_publish_page",
1866
+ user: toJson(req.user.simple()),
1867
+ });
1868
+ res.sendStatus(500);
1869
+ }
1870
+ else {
1871
+ log.info("Community Page Un-Published", {
1872
+ context: "un_publish_page",
1873
+ user: toJson(req.user.simple()),
1874
+ });
1875
+ res.sendStatus(200);
1876
+ }
1877
+ });
1878
+ });
1879
+ router.delete("/:groupId/:pageId/delete_page", auth.can("edit group"), function (req, res) {
1880
+ models.Page.deletePage(req, { group_id: req.params.groupId, id: req.params.pageId }, function (error) {
1881
+ if (error) {
1882
+ log.error("Could not delete page for admin for group", {
1883
+ err: error,
1884
+ context: "delete_page",
1885
+ user: toJson(req.user.simple()),
1886
+ });
1887
+ res.sendStatus(500);
1888
+ }
1889
+ else {
1890
+ log.info("Commuity Page Published", {
1891
+ context: "delete_page",
1892
+ user: toJson(req.user.simple()),
1893
+ });
1894
+ res.sendStatus(200);
1895
+ }
1896
+ });
1897
+ });
1898
+ router.post("/:groupId/post/news_story", auth.isLoggedInNoAnonymousCheck, auth.can("add to group"), function (req, res) {
1899
+ models.Point.createNewsStory(req, req.body, function (error) {
1900
+ if (error) {
1901
+ log.error("Could not save news story point on post", {
1902
+ err: error,
1903
+ context: "news_story",
1904
+ user: toJson(req.user.simple()),
1905
+ });
1906
+ res.sendStatus(500);
1907
+ }
1908
+ else {
1909
+ log.info("Point News Story Created", {
1910
+ context: "news_story",
1911
+ user: toJson(req.user.simple()),
1912
+ });
1913
+ res.sendStatus(200);
1914
+ }
1915
+ });
1916
+ });
1917
+ router.post("/:groupId/news_story", auth.isLoggedInNoAnonymousCheck, auth.can("add to group"), function (req, res) {
1918
+ models.Point.createNewsStory(req, req.body, function (error, point) {
1919
+ if (error) {
1920
+ log.error("Could not save news story point on group", {
1921
+ err: error,
1922
+ context: "news_story",
1923
+ user: toJson(req.user.simple()),
1924
+ });
1925
+ res.sendStatus(500);
1926
+ }
1927
+ else {
1928
+ log.info("Point News Story Created", {
1929
+ context: "news_story",
1930
+ user: toJson(req.user.simple()),
1931
+ });
1932
+ res.send({
1933
+ point_id: point ? point.id : null,
1934
+ });
1935
+ }
1936
+ });
1937
+ });
1938
+ router.get("/:groupId/admin_users", auth.can("edit group"), function (req, res) {
1939
+ models.Group.findOne({
1940
+ where: {
1941
+ id: req.params.groupId,
1942
+ },
1943
+ include: [
1944
+ {
1945
+ model: models.User,
1946
+ attributes: _.concat(models.User.defaultAttributesWithSocialMediaPublicAndEmail, ["created_at", "last_login_at"]),
1947
+ as: "GroupAdmins",
1948
+ through: { attributes: [] },
1949
+ required: true,
1950
+ include: [
1951
+ {
1952
+ model: models.Organization,
1953
+ attributes: ["id", "name"],
1954
+ as: "OrganizationUsers",
1955
+ required: false,
1956
+ },
1957
+ ],
1958
+ },
1959
+ ],
1960
+ })
1961
+ .then(function (group) {
1962
+ log.info("Got admin users", {
1963
+ context: "admin_users",
1964
+ user: toJson(req.user.simple()),
1965
+ });
1966
+ if (group) {
1967
+ res.send(group.GroupAdmins);
1968
+ }
1969
+ else {
1970
+ res.send([]);
1971
+ }
1972
+ })
1973
+ .catch(function (error) {
1974
+ log.error("Could not get admin users", {
1975
+ err: error,
1976
+ context: "admin_users",
1977
+ user: toJson(req.user.simple()),
1978
+ });
1979
+ res.sendStatus(500);
1980
+ });
1981
+ });
1982
+ router.get("/:groupId/promotion_users", auth.can("edit group"), function (req, res) {
1983
+ models.Group.findOne({
1984
+ where: {
1985
+ id: req.params.groupId,
1986
+ },
1987
+ include: [
1988
+ {
1989
+ model: models.User,
1990
+ attributes: _.concat(models.User.defaultAttributesWithSocialMediaPublicAndEmail, ["created_at", "last_login_at"]),
1991
+ as: "GroupPromoters",
1992
+ required: true,
1993
+ through: { attributes: [] },
1994
+ include: [
1995
+ {
1996
+ model: models.Organization,
1997
+ attributes: ["id", "name"],
1998
+ as: "OrganizationUsers",
1999
+ required: false,
2000
+ },
2001
+ ],
2002
+ },
2003
+ ],
2004
+ })
2005
+ .then(function (group) {
2006
+ log.info("Got promotion users", {
2007
+ context: "promotion_users",
2008
+ user: toJson(req.user.simple()),
2009
+ });
2010
+ if (group) {
2011
+ res.send(group.GroupPromoters);
2012
+ }
2013
+ else {
2014
+ res.send([]);
2015
+ }
2016
+ })
2017
+ .catch(function (error) {
2018
+ log.error("Could not get promotion users", {
2019
+ err: error,
2020
+ context: "promotion_users",
2021
+ user: toJson(req.user.simple()),
2022
+ });
2023
+ res.sendStatus(500);
2024
+ });
2025
+ });
2026
+ router.get("/:groupId/users", auth.can("edit group"), function (req, res) {
2027
+ models.Group.findOne({
2028
+ where: {
2029
+ id: req.params.groupId,
2030
+ },
2031
+ include: [
2032
+ {
2033
+ model: models.User,
2034
+ attributes: _.concat(models.User.defaultAttributesWithSocialMediaPublicAndEmail, ["created_at", "last_login_at"]),
2035
+ as: "GroupUsers",
2036
+ required: true,
2037
+ through: { attributes: [] },
2038
+ include: [
2039
+ {
2040
+ model: models.Organization,
2041
+ attributes: ["id", "name"],
2042
+ as: "OrganizationUsers",
2043
+ required: false,
2044
+ },
2045
+ ],
2046
+ },
2047
+ ],
2048
+ })
2049
+ .then(function (group) {
2050
+ log.info("Got users", {
2051
+ context: "users",
2052
+ user: toJson(req.user.simple()),
2053
+ });
2054
+ if (group) {
2055
+ res.send(group.GroupUsers);
2056
+ }
2057
+ else {
2058
+ res.send([]);
2059
+ }
2060
+ })
2061
+ .catch(function (error) {
2062
+ log.error("Could not get admin users", {
2063
+ err: error,
2064
+ context: "users",
2065
+ user: toJson(req.user.simple()),
2066
+ });
2067
+ res.sendStatus(500);
2068
+ });
2069
+ });
2070
+ router.get("/:groupId/default_post_image/:imageId", auth.can("view group"), function (req, res) {
2071
+ models.Image.findOne({
2072
+ where: {
2073
+ id: req.params.imageId,
2074
+ },
2075
+ })
2076
+ .then(function (image) {
2077
+ if (image) {
2078
+ var formats = JSON.parse(image.formats);
2079
+ res.redirect(formats[0]);
2080
+ }
2081
+ else {
2082
+ res.sendStatus(200);
2083
+ }
2084
+ })
2085
+ .catch(function (error) {
2086
+ log.error("Could not get image", {
2087
+ err: error,
2088
+ context: "post_default_image",
2089
+ });
2090
+ res.sendStatus(500);
2091
+ });
2092
+ });
2093
+ //TODO: Refactor this as not to repeate it in controlelrs
2094
+ const addAgentFabricUserToSessionIfNeeded = async (req) => {
2095
+ let userId = req.user && req.user.id ? req.user.id : null;
2096
+ if (!userId &&
2097
+ req.query.agentFabricUserId &&
2098
+ process.env.PS_TEMP_AGENTS_FABRIC_GROUP_API_KEY &&
2099
+ req.headers["x-api-key"] === process.env.PS_TEMP_AGENTS_FABRIC_GROUP_API_KEY) {
2100
+ log.info(`Creating group with temp agents fabric group api key ${req.query.agentFabricUserId}`);
2101
+ userId = req.query.agentFabricUserId;
2102
+ try {
2103
+ const loadedUser = await models.User.findByPk(userId);
2104
+ req.user = loadedUser;
2105
+ }
2106
+ catch (error) {
2107
+ log.error(`Could not find user with id ${userId}`, {
2108
+ context: "create",
2109
+ userId: userId,
2110
+ error: error,
2111
+ });
2112
+ throw error;
2113
+ }
2114
+ }
2115
+ else {
2116
+ log.info("Creating group with user id: " + userId);
2117
+ }
2118
+ };
2119
+ const copyThemeAndLogoFromAgentFabricGroup = async (newGroup, agentFabricGroup) => {
2120
+ if (agentFabricGroup.configuration && agentFabricGroup.configuration.theme) {
2121
+ newGroup.configuration.theme = { ...agentFabricGroup.configuration.theme };
2122
+ await newGroup.save();
2123
+ }
2124
+ if (agentFabricGroup.GroupLogoImages &&
2125
+ agentFabricGroup.GroupLogoImages.length > 0) {
2126
+ log.info("Copying logo images from agent fabric group");
2127
+ for (const logoImage of agentFabricGroup.GroupLogoImages) {
2128
+ log.info(`Copying logo image ${logoImage.id} from agent fabric group`);
2129
+ await newGroup.addGroupLogoImage(logoImage);
2130
+ }
2131
+ }
2132
+ };
2133
+ const createGroup = async (req, res) => {
2134
+ console.log("Creating group with community id: " + req.params.communityId);
2135
+ if (!req.user) {
2136
+ try {
2137
+ await addAgentFabricUserToSessionIfNeeded(req);
2138
+ }
2139
+ catch (error) {
2140
+ log.error("Could not add agent fabric user to session", {
2141
+ context: "create",
2142
+ userId: req.user.id,
2143
+ error: error,
2144
+ });
2145
+ res.sendStatus(500);
2146
+ return;
2147
+ }
2148
+ }
2149
+ var group = models.Group.build({
2150
+ name: req.body.name,
2151
+ objectives: req.body.objectives || req.body.description,
2152
+ access: models.Group.convertAccessFromRadioButtons(req.body),
2153
+ domain_id: req.ypDomain.id,
2154
+ user_id: req.user.id,
2155
+ community_id: req.params.communityId,
2156
+ user_agent: req.useragent.source,
2157
+ ip_address: req.clientIp,
2158
+ });
2159
+ group.theme_id = req.body.themeId ? parseInt(req.body.themeId) : null;
2160
+ if (req.body.in_group_folder_id) {
2161
+ group.in_group_folder_id = parseInt(req.body.in_group_folder_id);
2162
+ }
2163
+ if (req.body.is_group_folder && req.body.is_group_folder === "true") {
2164
+ group.is_group_folder = true;
2165
+ }
2166
+ group.access = models.Group.convertAccessFromRadioButtons(req.body);
2167
+ updateGroupConfigParameters(req, group);
2168
+ group
2169
+ .save()
2170
+ .then(async function (group) {
2171
+ log.info("Group Created", {
2172
+ groupId: group.id,
2173
+ context: "create",
2174
+ userId: req.user.id,
2175
+ });
2176
+ queue.add("process-similarities", { type: "update-collection", groupId: group.id }, "low");
2177
+ if (req.query.agentFabricGroupId) {
2178
+ try {
2179
+ const agentFabricGroup = await models.Group.findByPk(req.query.agentFabricGroupId, {
2180
+ include: [
2181
+ {
2182
+ model: models.Image,
2183
+ as: "GroupLogoImages",
2184
+ },
2185
+ ],
2186
+ });
2187
+ if (agentFabricGroup) {
2188
+ await copyThemeAndLogoFromAgentFabricGroup(group, agentFabricGroup);
2189
+ }
2190
+ }
2191
+ catch (error) {
2192
+ log.error("Error copying theme and logo from agent fabric group", {
2193
+ context: "create",
2194
+ newGroupId: group.id,
2195
+ agentFabricGroupId: req.query.agentFabricGroupId,
2196
+ error: error,
2197
+ });
2198
+ }
2199
+ }
2200
+ group.updateAllExternalCounters(req, "up", "counter_groups", function () {
2201
+ models.Group.addUserToGroupIfNeeded(group.id, req, function () {
2202
+ group.addGroupAdmins(req.user).then(function (results) {
2203
+ group.setupImages(req, group.id, function (error) {
2204
+ queue.add("process-moderation", {
2205
+ type: "estimate-collection-toxicity",
2206
+ collectionId: group.id,
2207
+ collectionType: "group",
2208
+ }, "high");
2209
+ queue.add("process-moderation", {
2210
+ type: "collection-review-and-annotate-images",
2211
+ collectionId: group.id,
2212
+ collectionType: "group",
2213
+ }, "medium");
2214
+ if (group.configuration.groupType == 3) {
2215
+ import("../agents/controllers/policySynthAgents.js").then(({ PolicySynthAgentsController }) => {
2216
+ PolicySynthAgentsController.setupApiKeysForGroup(group)
2217
+ .then(() => {
2218
+ log.info("Policy Synth Agents Api Keys Created", {
2219
+ groupId: group.id,
2220
+ context: "create",
2221
+ userId: req.user.id,
2222
+ });
2223
+ sendGroupOrError(res, group, "createGroup", req.user, error);
2224
+ })
2225
+ .catch((error) => {
2226
+ sendGroupOrError(res, group, "createGroup", req.user, error);
2227
+ log.error("Policy Synth Agents Api Keys Not Created", {
2228
+ groupId: group.id,
2229
+ context: "create",
2230
+ userId: req.user.id,
2231
+ error: error,
2232
+ });
2233
+ });
2234
+ });
2235
+ }
2236
+ else {
2237
+ sendGroupOrError(res, group, "createGroup", req.user, error);
2238
+ }
2239
+ });
2240
+ });
2241
+ });
2242
+ });
2243
+ })
2244
+ .catch(function (error) {
2245
+ sendGroupOrError(res, null, "create", req.user, error);
2246
+ });
2247
+ };
2248
+ router.put("/:id", auth.can("edit group"), function (req, res) {
2249
+ models.Group.findOne({
2250
+ where: { id: req.params.id },
2251
+ order: [
2252
+ [{ model: models.Image, as: "GroupLogoImages" }, "created_at", "asc"],
2253
+ [{ model: models.Image, as: "GroupHeaderImages" }, "created_at", "asc"],
2254
+ [{ model: models.Video, as: "GroupLogoVideos" }, "updated_at", "desc"],
2255
+ [
2256
+ { model: models.Video, as: "GroupLogoVideos" },
2257
+ { model: models.Image, as: "VideoImages" },
2258
+ "updated_at",
2259
+ "asc",
2260
+ ],
2261
+ ],
2262
+ include: [
2263
+ {
2264
+ model: models.Community,
2265
+ required: true,
2266
+ attributes: ["id", "access"],
2267
+ },
2268
+ {
2269
+ model: models.Image,
2270
+ as: "GroupLogoImages",
2271
+ attributes: models.Image.defaultAttributesPublic,
2272
+ required: false,
2273
+ },
2274
+ {
2275
+ model: models.Video,
2276
+ as: "GroupLogoVideos",
2277
+ attributes: ["id", "formats", "viewable", "public_meta"],
2278
+ required: false,
2279
+ include: [
2280
+ {
2281
+ model: models.Image,
2282
+ as: "VideoImages",
2283
+ attributes: ["formats", "updated_at"],
2284
+ required: false,
2285
+ },
2286
+ ],
2287
+ },
2288
+ {
2289
+ model: models.Image,
2290
+ as: "GroupHeaderImages",
2291
+ attributes: models.Image.defaultAttributesPublic,
2292
+ required: false,
2293
+ },
2294
+ ],
2295
+ })
2296
+ .then(function (group) {
2297
+ if (group) {
2298
+ group.name = req.body.name;
2299
+ group.objectives = req.body.objectives || req.body.description;
2300
+ group.theme_id = req.body.themeId ? parseInt(req.body.themeId) : null;
2301
+ group.access = models.Group.convertAccessFromRadioButtons(req.body);
2302
+ updateGroupConfigParameters(req, group);
2303
+ if (req.body.moveGroupTo) {
2304
+ moveGroupTo(req, group);
2305
+ }
2306
+ group
2307
+ .save()
2308
+ .then(function () {
2309
+ log.info("Group Updated", {
2310
+ group: toJson(group),
2311
+ context: "update",
2312
+ user: toJson(req.user),
2313
+ });
2314
+ queue.add("process-similarities", { type: "update-collection", groupId: group.id }, "low");
2315
+ group.setupImages(req, req.params.id, function (error) {
2316
+ queue.add("process-moderation", {
2317
+ type: "estimate-collection-toxicity",
2318
+ collectionId: group.id,
2319
+ collectionType: "group",
2320
+ }, "high");
2321
+ queue.add("process-moderation", {
2322
+ type: "collection-review-and-annotate-images",
2323
+ collectionId: group.id,
2324
+ collectionType: "group",
2325
+ }, "medium");
2326
+ if (group.configuration.groupType == 3) {
2327
+ import("../agents/controllers/policySynthAgents.js").then(({ PolicySynthAgentsController }) => {
2328
+ PolicySynthAgentsController.setupApiKeysForGroup(group)
2329
+ .then(() => {
2330
+ log.info("Policy Synth Agents Api Keys Created", {
2331
+ groupId: group.id,
2332
+ context: "create",
2333
+ userId: req.user.id,
2334
+ });
2335
+ sendGroupOrError(res, group, "setupImages", req.user, error);
2336
+ })
2337
+ .catch((error) => {
2338
+ sendGroupOrError(res, group, "createGroup", req.user, error);
2339
+ log.error("Policy Synth Agents Api Keys Not Created", {
2340
+ groupId: group.id,
2341
+ context: "create",
2342
+ userId: req.user.id,
2343
+ error: error,
2344
+ });
2345
+ });
2346
+ });
2347
+ }
2348
+ else {
2349
+ sendGroupOrError(res, group, "setupImages", req.user, error);
2350
+ }
2351
+ });
2352
+ })
2353
+ .catch(function (error) {
2354
+ sendGroupOrError(res, null, "update", req.user, error);
2355
+ });
2356
+ }
2357
+ else {
2358
+ sendGroupOrError(res, req.params.id, "update", req.user, "Not found", 404);
2359
+ }
2360
+ })
2361
+ .catch(function (error) {
2362
+ sendGroupOrError(res, null, "update", req.user, error);
2363
+ });
2364
+ });
2365
+ router.delete("/:id", auth.can("edit group"), function (req, res) {
2366
+ models.Group.findOne({
2367
+ where: { id: req.params.id },
2368
+ })
2369
+ .then(function (group) {
2370
+ if (group) {
2371
+ group.deleted = true;
2372
+ group.save().then(function () {
2373
+ log.info("Group Deleted", {
2374
+ group: toJson(group),
2375
+ context: "delete",
2376
+ user: toJson(req.user),
2377
+ });
2378
+ queue.add("process-similarities", { type: "update-collection", groupId: group.id }, "low");
2379
+ queue.add("process-deletion", {
2380
+ type: "delete-group-content",
2381
+ resetCounters: true,
2382
+ groupName: group.name,
2383
+ userId: req.user.id,
2384
+ groupId: group.id,
2385
+ }, "critical");
2386
+ group.updateAllExternalCounters(req, "down", "counter_groups", function () {
2387
+ res.sendStatus(200);
2388
+ });
2389
+ });
2390
+ }
2391
+ else {
2392
+ sendGroupOrError(res, req.params.id, "delete", req.user, "Not found", 404);
2393
+ }
2394
+ })
2395
+ .catch(function (error) {
2396
+ sendGroupOrError(res, null, "delete", req.user, error);
2397
+ });
2398
+ });
2399
+ router.delete("/:id/delete_content", auth.can("edit group"), function (req, res) {
2400
+ models.Group.findOne({
2401
+ where: { id: req.params.id },
2402
+ })
2403
+ .then(function (group) {
2404
+ if (group) {
2405
+ log.info("Group Delete Content", {
2406
+ group: toJson(group),
2407
+ context: "delete",
2408
+ user: toJson(req.user),
2409
+ });
2410
+ queue.add("process-deletion", {
2411
+ type: "delete-group-content",
2412
+ groupName: group.name,
2413
+ userId: req.user.id,
2414
+ groupId: group.id,
2415
+ useNotification: true,
2416
+ resetCounters: true,
2417
+ }, "critical");
2418
+ res.sendStatus(200);
2419
+ }
2420
+ else {
2421
+ sendGroupOrError(res, req.params.id, "delete", req.user, "Not found", 404);
2422
+ }
2423
+ })
2424
+ .catch(function (error) {
2425
+ sendGroupOrError(res, null, "delete", req.user, error);
2426
+ });
2427
+ });
2428
+ router.delete("/:id/anonymize_content", auth.can("edit group"), function (req, res) {
2429
+ const anonymizationDelayMs = 1000 * 60 * 60 * 24 * 7;
2430
+ models.Group.findOne({
2431
+ where: { id: req.params.id },
2432
+ })
2433
+ .then(function (group) {
2434
+ if (group) {
2435
+ log.info("Group Anonymize Content with delay", {
2436
+ group: toJson(group),
2437
+ anonymizationDelayMs: anonymizationDelayMs,
2438
+ context: "delete",
2439
+ userId: toJson(req.user.id),
2440
+ });
2441
+ queue.add("process-anonymization", {
2442
+ type: "notify-group-users",
2443
+ groupName: group.name,
2444
+ userId: req.user.id,
2445
+ groupId: group.id,
2446
+ delayMs: anonymizationDelayMs,
2447
+ }, "high");
2448
+ queue.add("process-anonymization", {
2449
+ type: "anonymize-group-content",
2450
+ groupName: group.name,
2451
+ userId: req.user.id,
2452
+ groupId: group.id,
2453
+ useNotification: true,
2454
+ resetCounters: true,
2455
+ }, "high", { delay: anonymizationDelayMs });
2456
+ res.sendStatus(200);
2457
+ }
2458
+ else {
2459
+ sendGroupOrError(res, req.params.id, "delete", req.user, "Not found", 404);
2460
+ }
2461
+ })
2462
+ .catch(function (error) {
2463
+ sendGroupOrError(res, null, "delete", req.user, error);
2464
+ });
2465
+ });
2466
+ router.post("/:id/clone", auth.can("edit group"), function (req, res) {
2467
+ models.Group.findOne({
2468
+ attributes: ["id", "community_id"],
2469
+ where: { id: req.params.id },
2470
+ include: [
2471
+ {
2472
+ model: models.Community,
2473
+ attributes: ["id", "domain_id"],
2474
+ include: [
2475
+ {
2476
+ model: models.Domain,
2477
+ attributes: ["id"],
2478
+ },
2479
+ ],
2480
+ },
2481
+ ],
2482
+ })
2483
+ .then(function (group) {
2484
+ if (group) {
2485
+ copyGroup(group.id, group.Community, group.Community.domain_id, { skipUsers: true, skipActivities: true }, (error, newGroup) => {
2486
+ if (error) {
2487
+ log.error("Group Cloned Failed", {
2488
+ error,
2489
+ groupId: req.params.id,
2490
+ });
2491
+ res.sendStatus(500);
2492
+ }
2493
+ else if (newGroup) {
2494
+ log.info("Group Cloned", { groupId: req.params.id });
2495
+ res.send({ id: newGroup.id });
2496
+ }
2497
+ else {
2498
+ log.error("Group Cloned succeeded but newGroup is missing", { groupId: req.params.id });
2499
+ res.sendStatus(500);
2500
+ }
2501
+ });
2502
+ }
2503
+ else {
2504
+ sendGroupOrError(res, req.params.id, "delete", req.user, "Not found", 404);
2505
+ }
2506
+ })
2507
+ .catch(function (error) {
2508
+ sendGroupOrError(res, null, "delete", req.user, error);
2509
+ });
2510
+ });
2511
+ router.get("/:id/checkNonOpenPosts", auth.can("view group"), (req, res) => {
2512
+ var PostsByNotOpen = models.Post.scope("not_open");
2513
+ PostsByNotOpen.count({ where: { group_id: req.params.id } })
2514
+ .then(function (count) {
2515
+ res.send({ hasNonOpenPosts: count != 0 });
2516
+ })
2517
+ .catch(function (error) {
2518
+ sendGroupOrError(res, null, "checkNonOpenPosts", req.user, error);
2519
+ });
2520
+ });
2521
+ router.get("/:id/configuration", auth.can("view group"), (req, res) => {
2522
+ models.Group.findOne({
2523
+ where: {
2524
+ id: req.params.id,
2525
+ },
2526
+ attributes: ["id", "configuration"],
2527
+ })
2528
+ .then((group) => {
2529
+ res.send(group.configuration);
2530
+ })
2531
+ .catch((error) => {
2532
+ sendGroupOrError(res, null, "configuration", req.user, error);
2533
+ });
2534
+ });
2535
+ const addVideosToGroup = (group, done) => {
2536
+ models.Video.findAll({
2537
+ attributes: ["id", "formats", "viewable", "created_at", "public_meta"],
2538
+ include: [
2539
+ {
2540
+ model: models.Image,
2541
+ as: "VideoImages",
2542
+ attributes: ["formats", "created_at"],
2543
+ required: false,
2544
+ },
2545
+ {
2546
+ model: models.Group,
2547
+ where: {
2548
+ id: group.id,
2549
+ },
2550
+ as: "GroupLogoVideos",
2551
+ required: true,
2552
+ attributes: ["id"],
2553
+ },
2554
+ ],
2555
+ order: [[{ model: models.Image, as: "VideoImages" }, "created_at", "asc"]],
2556
+ })
2557
+ .then((videos) => {
2558
+ group.dataValues.GroupLogoVideos = _.orderBy(videos, ["created_at"], ["desc"]);
2559
+ done();
2560
+ })
2561
+ .catch((error) => {
2562
+ done(error);
2563
+ });
2564
+ };
2565
+ router.get("/:groupFolderId/groupFolder", auth.can("view group"), function (req, res) {
2566
+ if (isValidDbId(req.params.groupFolderId)) {
2567
+ getGroupFolder(req, function (error, groupFolder) {
2568
+ if (error) {
2569
+ log.error("Could not get groupFolder", {
2570
+ err: error,
2571
+ groupFolderId: req.params.groupFolderId,
2572
+ user: req.user ? toJson(req.user.simple()) : null,
2573
+ });
2574
+ res.sendStatus(500);
2575
+ }
2576
+ else if (groupFolder) {
2577
+ res.send({ group: groupFolder });
2578
+ }
2579
+ else {
2580
+ res.sendStatus(404);
2581
+ }
2582
+ });
2583
+ }
2584
+ else {
2585
+ res.sendStatus(404);
2586
+ }
2587
+ });
2588
+ router.get("/:id", auth.can("view group"), function (req, res) {
2589
+ if (isValidDbId(req.params.id)) {
2590
+ models.Group.findOne({
2591
+ where: { id: req.params.id },
2592
+ attributes: models.Group.defaultAttributesPublic,
2593
+ order: [
2594
+ [{ model: models.Image, as: "GroupLogoImages" }, "created_at", "asc"],
2595
+ [{ model: models.Image, as: "GroupHeaderImages" }, "created_at", "asc"],
2596
+ [
2597
+ { model: models.Category },
2598
+ { model: models.Image, as: "CategoryIconImages" },
2599
+ "updated_at",
2600
+ "asc",
2601
+ ],
2602
+ [{ model: models.Category }, "name", "asc"],
2603
+ [
2604
+ { model: models.Community },
2605
+ { model: models.Image, as: "CommunityHeaderImages" },
2606
+ "created_at",
2607
+ "asc",
2608
+ ],
2609
+ ],
2610
+ include: [
2611
+ {
2612
+ model: models.Community,
2613
+ attributes: [
2614
+ "id",
2615
+ "theme_id",
2616
+ "name",
2617
+ "access",
2618
+ "google_analytics_code",
2619
+ "configuration",
2620
+ "language",
2621
+ "only_admins_can_create_groups",
2622
+ ],
2623
+ include: [
2624
+ {
2625
+ model: models.Domain,
2626
+ attributes: ["id", "theme_id", "name", "configuration"],
2627
+ },
2628
+ {
2629
+ model: models.Image,
2630
+ as: "CommunityHeaderImages",
2631
+ attributes: models.Image.defaultAttributesPublic,
2632
+ required: false,
2633
+ },
2634
+ ],
2635
+ },
2636
+ {
2637
+ model: models.Group,
2638
+ required: false,
2639
+ as: "GroupFolder",
2640
+ attributes: ["id", "name"],
2641
+ },
2642
+ {
2643
+ model: models.Category,
2644
+ required: false,
2645
+ include: [
2646
+ {
2647
+ model: models.Image,
2648
+ required: false,
2649
+ as: "CategoryIconImages",
2650
+ attributes: models.Image.defaultAttributesPublic,
2651
+ order: [
2652
+ [
2653
+ { model: models.Image, as: "CategoryIconImages" },
2654
+ "updated_at",
2655
+ "asc",
2656
+ ],
2657
+ ],
2658
+ },
2659
+ ],
2660
+ },
2661
+ {
2662
+ model: models.Image,
2663
+ as: "GroupLogoImages",
2664
+ attributes: models.Image.defaultAttributesPublic,
2665
+ required: false,
2666
+ },
2667
+ {
2668
+ model: models.Image,
2669
+ as: "GroupHeaderImages",
2670
+ attributes: models.Image.defaultAttributesPublic,
2671
+ required: false,
2672
+ },
2673
+ ],
2674
+ })
2675
+ .then(function (group) {
2676
+ if (group) {
2677
+ addVideosToGroup(group, (error) => {
2678
+ if (error) {
2679
+ sendGroupOrError(res, null, "count_posts", req.user, error);
2680
+ }
2681
+ else {
2682
+ log.info("Group Viewed", {
2683
+ id: group.id,
2684
+ userId: req.user ? req.user.id : -1,
2685
+ });
2686
+ var PostsByNotOpen = models.Post.scope("not_open");
2687
+ PostsByNotOpen.count({ where: { group_id: req.params.id } })
2688
+ .then(function (count) {
2689
+ res.send({ group: group, hasNonOpenPosts: count != 0 });
2690
+ })
2691
+ .catch(function (error) {
2692
+ sendGroupOrError(res, null, "count_posts", req.user, error);
2693
+ });
2694
+ }
2695
+ });
2696
+ }
2697
+ else {
2698
+ sendGroupOrError(res, req.params.id, "view", req.user, "Not found", 404);
2699
+ }
2700
+ return null;
2701
+ })
2702
+ .catch(function (error) {
2703
+ sendGroupOrError(res, null, "view", req.user, error);
2704
+ });
2705
+ }
2706
+ else {
2707
+ res.sendStatus(404);
2708
+ }
2709
+ });
2710
+ const allowedTextTypesForGroup = [
2711
+ "alternativeTextForNewIdeaButton",
2712
+ "alternativeTextForNewIdeaButtonClosed",
2713
+ "alternativeTextForNewIdeaButtonHeader",
2714
+ "alternativeTextForNewIdeaSaveButton",
2715
+ "customCategoryQuestionText",
2716
+ "alternativePointForHeader",
2717
+ "customThankYouTextNewPosts",
2718
+ "customTitleQuestionText",
2719
+ "customFilterText",
2720
+ "customTabTitleNewLocation",
2721
+ "alternativePointAgainstHeader",
2722
+ "alternativePointForLabel",
2723
+ "alternativePointAgainstLabel",
2724
+ "customAdminCommentsTitle",
2725
+ "urlToReviewActionText",
2726
+ "aoiWelcomeMessage",
2727
+ "aoiWelcomeHtml",
2728
+ "aoiQuestionText",
2729
+ "aoiAnswerText",
2730
+ ];
2731
+ router.get("/:id/translatedText", auth.can("view group"), function (req, res) {
2732
+ if (req.query.textType.indexOf("group") > -1 ||
2733
+ allowedTextTypesForGroup.indexOf(req.query.textType) > -1) {
2734
+ models.Group.findOne({
2735
+ where: {
2736
+ id: req.params.id,
2737
+ },
2738
+ attributes: ["id", "name", "objectives", "configuration"],
2739
+ })
2740
+ .then(function (group) {
2741
+ if (group) {
2742
+ models.AcTranslationCache.getTranslation(req, group, function (error, translation) {
2743
+ if (error) {
2744
+ sendGroupOrError(res, req.params.id, "translated", req.user, error, 500);
2745
+ }
2746
+ else {
2747
+ res.send(translation);
2748
+ }
2749
+ });
2750
+ log.info("Group translatedTitle", { context: "translated" });
2751
+ }
2752
+ else {
2753
+ sendGroupOrError(res, req.params.id, "translated", req.user, "Not found", 404);
2754
+ }
2755
+ })
2756
+ .catch(function (error) {
2757
+ sendGroupOrError(res, null, "translated", req.user, error);
2758
+ });
2759
+ }
2760
+ else {
2761
+ sendGroupOrError(res, req.params.id, "translated", req.user, "Wrong textType", 401);
2762
+ }
2763
+ });
2764
+ router.get("/:id/translatedSurveyQuestions", auth.can("view group"), function (req, res) {
2765
+ const targetLanguage = req.query.targetLanguage;
2766
+ models.AcTranslationCache.getSurveyQuestionTranslations(req.params.id, targetLanguage, (error, translations) => {
2767
+ if (error) {
2768
+ sendGroupOrError(res, req.params.id, "translatedSurveyQuestions", req.user, error, 500);
2769
+ }
2770
+ else {
2771
+ res.send(translations);
2772
+ }
2773
+ });
2774
+ });
2775
+ router.get("/:id/translatedRegistrationQuestions", auth.can("view group"), function (req, res) {
2776
+ const targetLanguage = req.query.targetLanguage;
2777
+ models.AcTranslationCache.getRegistrationQuestionTranslations(req.params.id, targetLanguage, (error, translations) => {
2778
+ if (error) {
2779
+ sendGroupOrError(res, req.params.id, "translatedRegistrationQuestions", req.user, error, 500);
2780
+ }
2781
+ else {
2782
+ res.send(translations);
2783
+ }
2784
+ });
2785
+ });
2786
+ router.get("/:id/search/:term", auth.can("view group"), function (req, res) {
2787
+ log.info("Group Search", {
2788
+ groupId: req.params.id,
2789
+ context: "view",
2790
+ user: toJson(req.user),
2791
+ });
2792
+ models.Post.search(req.params.term, req.params.id, models.Category).then(function (posts) {
2793
+ posts = _.reject(posts, function (post) {
2794
+ return post.deleted == true;
2795
+ });
2796
+ res.send({
2797
+ posts: posts,
2798
+ totalPostsCount: posts.length,
2799
+ });
2800
+ });
2801
+ });
2802
+ var getPostsWithAllFromIds = function (postsWithIds, postOrder, done) {
2803
+ var collectedIds = _.map(postsWithIds, function (post) {
2804
+ return post.id;
2805
+ });
2806
+ let posts;
2807
+ let videos;
2808
+ async.parallel([
2809
+ (parallelCallback) => {
2810
+ models.Post.findAll({
2811
+ where: {
2812
+ id: {
2813
+ $in: collectedIds,
2814
+ },
2815
+ },
2816
+ attributes: [
2817
+ "id",
2818
+ "name",
2819
+ "description",
2820
+ "public_data",
2821
+ "status",
2822
+ "content_type",
2823
+ "official_status",
2824
+ "counter_endorsements_up",
2825
+ "cover_media_type",
2826
+ "counter_endorsements_down",
2827
+ "group_id",
2828
+ "language",
2829
+ "counter_points",
2830
+ "counter_flags",
2831
+ "location",
2832
+ "created_at",
2833
+ "category_id",
2834
+ ],
2835
+ order: [models.sequelize.literal(postOrder)],
2836
+ include: [
2837
+ {
2838
+ model: models.Category,
2839
+ attributes: { exclude: ["ip_address", "user_agent"] },
2840
+ required: false,
2841
+ include: [
2842
+ {
2843
+ model: models.Image,
2844
+ required: false,
2845
+ attributes: models.Image.defaultAttributesPublic,
2846
+ as: "CategoryIconImages",
2847
+ },
2848
+ ],
2849
+ },
2850
+ {
2851
+ model: models.Audio,
2852
+ required: false,
2853
+ attributes: ["id", "formats", "updated_at", "listenable"],
2854
+ as: "PostAudios",
2855
+ },
2856
+ {
2857
+ model: models.User,
2858
+ required: false,
2859
+ attributes: models.User.defaultAttributesWithSocialMediaPublic,
2860
+ include: [
2861
+ {
2862
+ model: models.Image,
2863
+ as: "UserProfileImages",
2864
+ attributes: ["id", "formats", "updated_at"],
2865
+ required: false,
2866
+ },
2867
+ ],
2868
+ },
2869
+ {
2870
+ model: models.Group,
2871
+ required: true,
2872
+ attributes: ["id", "configuration", "name", "theme_id", "access"],
2873
+ include: [
2874
+ {
2875
+ model: models.Category,
2876
+ required: false,
2877
+ },
2878
+ {
2879
+ model: models.Community,
2880
+ attributes: [
2881
+ "id",
2882
+ "name",
2883
+ "theme_id",
2884
+ "access",
2885
+ "google_analytics_code",
2886
+ "configuration",
2887
+ "only_admins_can_create_groups",
2888
+ ],
2889
+ required: true,
2890
+ },
2891
+ ],
2892
+ },
2893
+ {
2894
+ model: models.Image,
2895
+ attributes: models.Image.defaultAttributesPublic,
2896
+ as: "PostHeaderImages",
2897
+ required: false,
2898
+ },
2899
+ ],
2900
+ })
2901
+ .then(function (postsIn) {
2902
+ posts = postsIn;
2903
+ log.info("FINISHED POSTS A");
2904
+ posts.forEach((post) => {
2905
+ if (post.PostHeaderImages) {
2906
+ post.PostHeaderImages = _.orderBy(post.PostHeaderImages, ["updated_at"], ["asc"]);
2907
+ }
2908
+ if (post.Category && post.Category.CategoryIconImages) {
2909
+ post.Category.CategoryIconImages = _.orderBy(post.Category.CategoryIconImages, ["updated_at"], ["asc"]);
2910
+ }
2911
+ if (post.PostAudios) {
2912
+ post.PostAudios = _.orderBy(post.PostAudios, ["updated_at"], ["desc"]);
2913
+ }
2914
+ if (post.Group && post.Group.Categories) {
2915
+ post.Group.Categories = _.orderBy(post.Group.Categories, ["name"], ["asc"]);
2916
+ }
2917
+ });
2918
+ parallelCallback();
2919
+ })
2920
+ .catch(function (error) {
2921
+ parallelCallback(error);
2922
+ });
2923
+ },
2924
+ (parallelCallback) => {
2925
+ models.Post.getVideosForPosts(collectedIds, (error, videosIn) => {
2926
+ log.info("GOT VIDEOS 1");
2927
+ if (error) {
2928
+ parallelCallback(error);
2929
+ }
2930
+ else {
2931
+ videos = videosIn;
2932
+ parallelCallback();
2933
+ }
2934
+ });
2935
+ },
2936
+ ], (error) => {
2937
+ if (error) {
2938
+ done(error);
2939
+ }
2940
+ else {
2941
+ if (videos && videos.length > 0) {
2942
+ log.info("GOT VIDEOS 2");
2943
+ models.Post.addVideosToAllPosts(posts, videos);
2944
+ }
2945
+ else if (!videos) {
2946
+ log.error("No videos for getVideosForPosts");
2947
+ }
2948
+ log.info("FINISHED POSTS B");
2949
+ done(null, posts);
2950
+ }
2951
+ });
2952
+ };
2953
+ router.get("/:id/posts/:filter/:categoryId/:status?", auth.can("view group"), function (req, res) {
2954
+ const redisKey = `cache:posts:${req.params.id}:${req.params.filter}:${req.params.categoryId}:${req.params.status}:${req.query.offset}:${req.query.randomSeed}`;
2955
+ req.redisClient
2956
+ .get(redisKey)
2957
+ .then((postsInfo) => {
2958
+ if (postsInfo) {
2959
+ res.send(JSON.parse(postsInfo));
2960
+ }
2961
+ else {
2962
+ models.Group.findOne({
2963
+ where: {
2964
+ id: req.params.id,
2965
+ },
2966
+ attributes: ["id", "configuration"],
2967
+ })
2968
+ .then((group) => {
2969
+ var where = { group_id: req.params.id, deleted: false };
2970
+ let attributes = [
2971
+ "id",
2972
+ "status",
2973
+ "name",
2974
+ "official_status",
2975
+ "language",
2976
+ "counter_endorsements_up",
2977
+ "counter_endorsements_down",
2978
+ "created_at",
2979
+ ];
2980
+ const includes = [];
2981
+ var postOrder = "(counter_endorsements_up-counter_endorsements_down) DESC";
2982
+ if (req.params.filter === "newest") {
2983
+ postOrder = "created_at DESC";
2984
+ }
2985
+ else if (req.params.filter === "most_debated") {
2986
+ postOrder = "counter_points DESC";
2987
+ }
2988
+ else if (req.params.filter === "random") {
2989
+ postOrder = "created_at DESC";
2990
+ }
2991
+ else if (req.params.filter === "oldest") {
2992
+ postOrder = "created_at ASC";
2993
+ }
2994
+ else if (req.params.filter === "alphabetical") {
2995
+ postOrder = "name ASC";
2996
+ }
2997
+ let postOrderFinal = [models.sequelize.literal(postOrder)];
2998
+ let seqGroup = null;
2999
+ let ratingOrderNeeded = false;
3000
+ let limit = 20;
3001
+ var offset = 0;
3002
+ if (req.query.offset) {
3003
+ offset = parseInt(req.query.offset);
3004
+ }
3005
+ let ratingOffset = offset;
3006
+ const ratingsPostLookup = {};
3007
+ if (req.params.filter === "top" &&
3008
+ group.configuration &&
3009
+ group.configuration.customRatings != null &&
3010
+ group.configuration.customRatings.length > 0) {
3011
+ const attrIncludes = [];
3012
+ attrIncludes.push([
3013
+ models.sequelize.literal(`(
3014
+ SELECT AVG(value)
3015
+ FROM ratings AS rating
3016
+ WHERE
3017
+ rating.post_id = "Post".id
3018
+ )`),
3019
+ "RatingAverage",
3020
+ ]);
3021
+ attrIncludes.push([
3022
+ models.sequelize.literal(`(
3023
+ SELECT COUNT(*)
3024
+ FROM ratings AS rating
3025
+ WHERE
3026
+ rating.post_id = "Post".id
3027
+ )`),
3028
+ "RatingCount",
3029
+ ]);
3030
+ //TODO: Include also just the attributes from above
3031
+ attributes = {
3032
+ include: attrIncludes,
3033
+ };
3034
+ //postOrderFinal = models.sequelize.literal("RatingAverage ASC");
3035
+ ratingOrderNeeded = true;
3036
+ //TODO: Get postgres ordering working with a count limit ORDER BY flag CASE
3037
+ limit = 1500;
3038
+ offset = 0;
3039
+ }
3040
+ if (req.params.categoryId != "null") {
3041
+ where["category_id"] = req.params.categoryId;
3042
+ }
3043
+ log.info("Posts", {
3044
+ gId: req.params.id,
3045
+ f: req.params.filter,
3046
+ cId: req.params.categoryId,
3047
+ uId: req.user ? req.user.id : -1,
3048
+ });
3049
+ if (["open", "failed", "successful", "in_progress"].indexOf(req.params.status) > -1) {
3050
+ var PostsByStatus = models.Post.scope(req.params.status);
3051
+ PostsByStatus.findAndCountAll({
3052
+ where: where,
3053
+ attributes: attributes,
3054
+ include: includes,
3055
+ group: seqGroup,
3056
+ order: postOrderFinal,
3057
+ limit: limit,
3058
+ offset: offset,
3059
+ })
3060
+ .then(function (postResults) {
3061
+ log.info("Posts 2");
3062
+ const posts = postResults.rows;
3063
+ var totalPostsCount = postResults.count;
3064
+ var postRows = posts;
3065
+ if (req.params.filter === "random" &&
3066
+ req.query.randomSeed &&
3067
+ postRows.length > 0) {
3068
+ postRows = seededShuffle(postRows, req.query.randomSeed);
3069
+ }
3070
+ if (ratingOrderNeeded) {
3071
+ postRows = _.forEach(postRows, (post) => {
3072
+ if (post.dataValues.RatingCount &&
3073
+ post.dataValues.RatingAverage) {
3074
+ const ratingCount = parseInt(post.dataValues.RatingCount);
3075
+ const ratingAverage = parseFloat(post.dataValues.RatingAverage);
3076
+ // More than one round of full ratings for the rating to count towards top rating calc
3077
+ if (ratingCount >
3078
+ group.configuration.customRatings.length) {
3079
+ ratingsPostLookup[post.dataValues.id] =
3080
+ ratingAverage;
3081
+ }
3082
+ else {
3083
+ ratingsPostLookup[post.dataValues.id] = 0.0;
3084
+ }
3085
+ }
3086
+ else {
3087
+ ratingsPostLookup[post.dataValues.id] = 0.0;
3088
+ }
3089
+ });
3090
+ postRows = _.orderBy(postRows, [
3091
+ (post) => {
3092
+ return ratingsPostLookup[post.dataValues.id];
3093
+ },
3094
+ ], ["desc"]);
3095
+ if (ratingOffset) {
3096
+ postRows = _.drop(postRows, ratingOffset);
3097
+ }
3098
+ if (postRows.length > 20) {
3099
+ postRows = _.dropRight(postRows, postRows.length - 20);
3100
+ }
3101
+ totalPostsCount = postResults.rows.length;
3102
+ }
3103
+ log.info("Posts 3");
3104
+ getPostsWithAllFromIds(postRows, postOrder, function (error, finalRows) {
3105
+ log.info("Posts 4");
3106
+ if (error) {
3107
+ log.error("Error getting group", { err: error });
3108
+ res.sendStatus(500);
3109
+ }
3110
+ else {
3111
+ if (req.params.filter === "random" &&
3112
+ req.query.randomSeed &&
3113
+ finalRows &&
3114
+ finalRows.length > 0) {
3115
+ finalRows = seededShuffle(finalRows, req.query.randomSeed);
3116
+ }
3117
+ else if (ratingOrderNeeded) {
3118
+ finalRows = _.orderBy(finalRows, [
3119
+ (post) => {
3120
+ return ratingsPostLookup[post.dataValues.id];
3121
+ },
3122
+ ], ["desc"]);
3123
+ }
3124
+ models.Post.setOrganizationUsersForPosts(finalRows, (error) => {
3125
+ log.info("Posts 2");
3126
+ if (error) {
3127
+ log.error("Error getting group", {
3128
+ err: error,
3129
+ });
3130
+ res.sendStatus(500);
3131
+ }
3132
+ else {
3133
+ const postsOut = {
3134
+ posts: finalRows,
3135
+ totalPostsCount: totalPostsCount,
3136
+ };
3137
+ req.redisClient.setEx(redisKey, process.env.POSTS_CACHE_TTL
3138
+ ? parseInt(process.env.POSTS_CACHE_TTL)
3139
+ : 3, JSON.stringify(postsOut));
3140
+ res.send(postsOut);
3141
+ }
3142
+ });
3143
+ }
3144
+ });
3145
+ })
3146
+ .catch(function (error) {
3147
+ log.error("Error getting group", { err: error });
3148
+ res.sendStatus(500);
3149
+ });
3150
+ }
3151
+ else {
3152
+ log.error("Cant find status", { status: req.params.status });
3153
+ res.sendStatus(404);
3154
+ }
3155
+ })
3156
+ .catch((errorGroup) => {
3157
+ sendGroupOrError(res, null, "getPostsFromGroup Group.find", req.user, errorGroup);
3158
+ });
3159
+ }
3160
+ })
3161
+ .catch((error) => {
3162
+ sendGroupOrError(res, null, "getPostsFromGroup", req.user, error);
3163
+ });
3164
+ });
3165
+ router.get("/:id/categories", auth.can("view group"), function (req, res) {
3166
+ models.Category.findAll({
3167
+ where: { group_id: req.params.id },
3168
+ limit: 20,
3169
+ })
3170
+ .then(function (categories) {
3171
+ if (categories) {
3172
+ log.info("Group Categories Viewed", {
3173
+ group: req.params.id,
3174
+ context: "view",
3175
+ user: toJson(req.user),
3176
+ });
3177
+ res.send(categories);
3178
+ }
3179
+ else {
3180
+ sendGroupOrError(res, req.params.id, "view", req.user, "Not found", 404);
3181
+ }
3182
+ })
3183
+ .catch(function (error) {
3184
+ sendGroupOrError(res, null, "view categories", req.user, error);
3185
+ });
3186
+ });
3187
+ router.get("/:id/post_locations", auth.can("view group"), function (req, res) {
3188
+ models.Post.findAll({
3189
+ where: {
3190
+ location: {
3191
+ $ne: null,
3192
+ },
3193
+ group_id: req.params.id,
3194
+ },
3195
+ attributes: models.Post.defaultAttributesPublic,
3196
+ order: [
3197
+ [{ model: models.Image, as: "PostHeaderImages" }, "updated_at", "asc"],
3198
+ ],
3199
+ include: [
3200
+ {
3201
+ model: models.Image,
3202
+ as: "PostHeaderImages",
3203
+ attributes: models.Image.defaultAttributesPublic,
3204
+ required: false,
3205
+ },
3206
+ {
3207
+ model: models.Group,
3208
+ attributes: ["id", "configuration"],
3209
+ required: true,
3210
+ },
3211
+ ],
3212
+ select: ["id", "name", "location"],
3213
+ })
3214
+ .then(function (posts) {
3215
+ if (posts) {
3216
+ log.info("Group Post Locations Viewed", {
3217
+ communityId: req.params.id,
3218
+ userId: req.user ? req.user.id : -1,
3219
+ });
3220
+ var collectedIds = _.map(posts, function (post) {
3221
+ return post.id;
3222
+ });
3223
+ models.Post.getVideosForPosts(collectedIds, (error, videos) => {
3224
+ if (error) {
3225
+ sendGroupOrError(res, null, "view post locations", req.user, "Not found", 404);
3226
+ }
3227
+ else {
3228
+ if (videos.length > 0) {
3229
+ models.Post.addVideosToAllPosts(posts, videos);
3230
+ }
3231
+ res.send(posts);
3232
+ }
3233
+ });
3234
+ }
3235
+ else {
3236
+ sendGroupOrError(res, null, "view post locations", req.user, "Not found", 404);
3237
+ }
3238
+ })
3239
+ .catch(function (error) {
3240
+ sendGroupOrError(res, null, "view post locations", req.user, error);
3241
+ });
3242
+ });
3243
+ router.get("/:id/categories_count/:tabName", auth.can("view group"), function (req, res) {
3244
+ var categoriesCount, allPostCount;
3245
+ var status = null;
3246
+ if (req.params.tabName === "failed") {
3247
+ status = -2;
3248
+ }
3249
+ else if (req.params.tabName === "open") {
3250
+ status = 0;
3251
+ }
3252
+ else if (req.params.tabName === "in_progress") {
3253
+ status = -1;
3254
+ }
3255
+ else if (req.params.tabName === "successful") {
3256
+ status = 2;
3257
+ }
3258
+ if (status !== null) {
3259
+ async.parallel([
3260
+ function (parallelCallback) {
3261
+ models.Post.count({
3262
+ attributes: ["category_id"],
3263
+ where: {
3264
+ group_id: req.params.id,
3265
+ official_status: status,
3266
+ },
3267
+ include: [
3268
+ {
3269
+ model: models.Category,
3270
+ },
3271
+ ],
3272
+ group: ["category_id"],
3273
+ })
3274
+ .then(function (results) {
3275
+ categoriesCount = results;
3276
+ parallelCallback();
3277
+ })
3278
+ .catch(function (error) {
3279
+ parallelCallback(error);
3280
+ });
3281
+ },
3282
+ function (parallelCallback) {
3283
+ models.Post.count({
3284
+ where: {
3285
+ group_id: req.params.id,
3286
+ official_status: status,
3287
+ },
3288
+ })
3289
+ .then(function (count) {
3290
+ allPostCount = count;
3291
+ parallelCallback();
3292
+ })
3293
+ .catch(function (error) {
3294
+ parallelCallback(error);
3295
+ });
3296
+ },
3297
+ ], function (error) {
3298
+ if (error) {
3299
+ sendGroupOrError(res, null, "categories_count", req.user, error);
3300
+ }
3301
+ else {
3302
+ res.send({
3303
+ categoriesCount: categoriesCount,
3304
+ allPostCount: allPostCount,
3305
+ });
3306
+ }
3307
+ });
3308
+ }
3309
+ else {
3310
+ sendGroupOrError(res, null, "categories_count", req.user, "Cant find status for posts");
3311
+ }
3312
+ });
3313
+ router.put("/:id/:groupId/mergeWithGroup", auth.can("edit post"), function (req, res) {
3314
+ auth.authNeedsGroupAdminForCreate({ id: req.params.groupId }, req, function (error, isAuthorized) {
3315
+ if (isAuthorized) {
3316
+ var inGroup, outGroup, post, outCommunityId, outDomainId;
3317
+ async.series([
3318
+ function (callback) {
3319
+ models.Group.findOne({
3320
+ where: {
3321
+ id: req.params.id,
3322
+ },
3323
+ include: [
3324
+ {
3325
+ model: models.Community,
3326
+ required: true,
3327
+ include: [
3328
+ {
3329
+ model: models.Domain,
3330
+ required: true,
3331
+ attributes: models.Domain.defaultAttributesPublic,
3332
+ },
3333
+ ],
3334
+ },
3335
+ ],
3336
+ })
3337
+ .then(function (group) {
3338
+ inGroup = groupIn;
3339
+ callback();
3340
+ })
3341
+ .catch(function (error) {
3342
+ callback(error);
3343
+ });
3344
+ },
3345
+ function (callback) {
3346
+ models.Group.findOne({
3347
+ where: {
3348
+ id: req.params.groupId,
3349
+ },
3350
+ include: [
3351
+ {
3352
+ model: models.Community,
3353
+ required: true,
3354
+ include: [
3355
+ {
3356
+ model: models.Domain,
3357
+ required: true,
3358
+ attributes: models.Domain.defaultAttributesPublic,
3359
+ },
3360
+ ],
3361
+ },
3362
+ ],
3363
+ })
3364
+ .then(function (group) {
3365
+ outGroup = group;
3366
+ outCommunityId = group.Community.id;
3367
+ outDomainId = group.Community.Domain.id;
3368
+ callback();
3369
+ })
3370
+ .catch(function (error) {
3371
+ callback(error);
3372
+ });
3373
+ },
3374
+ function (callback) {
3375
+ models.Post.findAll({
3376
+ where: {
3377
+ group_id: inGroup.id,
3378
+ },
3379
+ })
3380
+ .then(function (posts) {
3381
+ async.eachSeries(posts, function (post, seriesCallback) {
3382
+ post.set("group_id", outGroup.id);
3383
+ post.save().then(function (results) {
3384
+ console.log("Have changed group id");
3385
+ models.AcActivity.findAll({
3386
+ where: {
3387
+ post_id: post.id,
3388
+ },
3389
+ })
3390
+ .then(function (activities) {
3391
+ async.eachSeries(activities, function (activity, innerSeriesCallback) {
3392
+ activity.set("group_id", outGroup.id);
3393
+ activity.set("community_id", outCommunityId);
3394
+ activity.set("domain_id", outDomainId);
3395
+ activity.save().then(function (results) {
3396
+ console.log("Have changed group and all: " +
3397
+ activity.id);
3398
+ innerSeriesCallback();
3399
+ });
3400
+ }, function (error) {
3401
+ seriesCallback(error);
3402
+ });
3403
+ })
3404
+ .catch(function (error) {
3405
+ seriesCallback(error);
3406
+ });
3407
+ }, function (error) {
3408
+ callback(error);
3409
+ });
3410
+ });
3411
+ })
3412
+ .catch(function (error) {
3413
+ callback(error);
3414
+ });
3415
+ },
3416
+ ], function (error) {
3417
+ if (error) {
3418
+ log.error("Merge with group", {
3419
+ groupId: req.params.id,
3420
+ groupToId: req.params.groupId,
3421
+ });
3422
+ res.sendStatus(500);
3423
+ }
3424
+ else {
3425
+ log.info("Merge with group", {
3426
+ groupId: req.params.id,
3427
+ groupToId: req.params.groupId,
3428
+ });
3429
+ res.sendStatus(200);
3430
+ }
3431
+ });
3432
+ }
3433
+ else {
3434
+ log.error("Merge with group", {
3435
+ groupId: req.params.id,
3436
+ groupToId: req.params.groupId,
3437
+ });
3438
+ res.sendStatus(401);
3439
+ }
3440
+ });
3441
+ });
3442
+ // Moderation
3443
+ router.delete("/:groupId/:itemId/:itemType/:actionType/process_one_moderation_item", auth.can("edit group"), (req, res) => {
3444
+ performSingleModerationAction(req, res, {
3445
+ groupId: req.params.groupId,
3446
+ itemId: req.params.itemId,
3447
+ itemType: req.params.itemType,
3448
+ actionType: req.params.actionType,
3449
+ });
3450
+ });
3451
+ router.delete("/:groupId/:actionType/process_many_moderation_item", auth.can("edit group"), (req, res) => {
3452
+ queue.add("process-moderation", {
3453
+ type: "perform-many-moderation-actions",
3454
+ items: req.body.items,
3455
+ actionType: req.params.actionType,
3456
+ groupId: req.params.groupId,
3457
+ }, "critical");
3458
+ res.send({});
3459
+ });
3460
+ router.get("/:groupId/flagged_content", auth.can("edit group"), (req, res) => {
3461
+ getAllModeratedItemsByGroup({ groupId: req.params.groupId }, (error, items) => {
3462
+ if (error) {
3463
+ log.error("Error getting items for moderation", { error });
3464
+ res.sendStatus(500);
3465
+ }
3466
+ else {
3467
+ res.send(items);
3468
+ }
3469
+ });
3470
+ });
3471
+ router.get("/:groupId/moderate_all_content", auth.can("edit group"), (req, res) => {
3472
+ getAllModeratedItemsByGroup({ groupId: req.params.groupId, allContent: true }, (error, items) => {
3473
+ if (error) {
3474
+ log.error("Error getting items for moderation", { error });
3475
+ res.sendStatus(500);
3476
+ }
3477
+ else {
3478
+ res.send(items);
3479
+ }
3480
+ });
3481
+ });
3482
+ router.get("/:groupId/flagged_content_count", auth.can("edit group"), (req, res) => {
3483
+ countAllModeratedItemsByGroup({ groupId: req.params.groupId }, (error, count) => {
3484
+ if (error) {
3485
+ log.error("Error getting items for moderation", { error });
3486
+ res.sendStatus(500);
3487
+ }
3488
+ else {
3489
+ res.send({ count });
3490
+ }
3491
+ });
3492
+ });
3493
+ // CAMPAIGNS
3494
+ router.post("/:id/campaignCreateAndStart", auth.can("edit group"), (req, res) => {
3495
+ models.AcCampaign.createAndSendCampaign(req.body, (error) => {
3496
+ if (error) {
3497
+ log.error("Group createCampaign error", {
3498
+ context: "campaignCreateAndStart",
3499
+ params: req.body,
3500
+ error,
3501
+ });
3502
+ res.sendStatus(500);
3503
+ }
3504
+ else {
3505
+ log.info("Group createCampaign completed", {
3506
+ context: "campaignCreateAndStart",
3507
+ });
3508
+ res.sendStatus(200);
3509
+ }
3510
+ });
3511
+ });
3512
+ router.get("/:groupId/:jobId/getCampaignSendStatus", auth.can("edit group"), function (req, res) {
3513
+ models.AcCampaign.getSendStatus(req.params.jobId, (error, results) => {
3514
+ if (error) {
3515
+ log.error("Group createCampaign error", {
3516
+ context: "getCampaignSendStatus",
3517
+ error,
3518
+ });
3519
+ res.sendStatus(500);
3520
+ }
3521
+ else {
3522
+ log.info("Group getCampaignSendStatus completed", {
3523
+ context: "getCampaignSendStatus",
3524
+ });
3525
+ res.sendresults;
3526
+ }
3527
+ });
3528
+ });
3529
+ router.get("/:id/getCampaigns", auth.can("edit group"), (req, res) => {
3530
+ models.AcCampaign.find({
3531
+ where: {
3532
+ group_id: req.params.id,
3533
+ },
3534
+ })
3535
+ .then((campaigns) => {
3536
+ res.send(campaigns);
3537
+ })
3538
+ .catch((error) => {
3539
+ log.info("Group createCampaign error", {
3540
+ context: "createCampaign",
3541
+ params: req.body,
3542
+ error,
3543
+ });
3544
+ res.sendStatus(500);
3545
+ });
3546
+ });
3547
+ // LISTS
3548
+ router.get("/:id/getList", auth.can("edit group"), (req, res) => {
3549
+ models.AcList.getList(req.params.id, (error, results) => {
3550
+ if (error) {
3551
+ log.error("Group getList error", { context: "getList", error });
3552
+ res.sendStatus(500);
3553
+ }
3554
+ else {
3555
+ log.info("Group getList completed", { context: "getList" });
3556
+ res.send(results);
3557
+ }
3558
+ });
3559
+ });
3560
+ router.put("/:id/:listId/addListUsers", auth.can("edit group"), (req, res) => {
3561
+ models.AcList.addListUsers(req.params.listId, req.body, (error, results) => {
3562
+ if (error) {
3563
+ log.error("Group addListUsers error", { context: "addListUsers", error });
3564
+ res.sendStatus(500);
3565
+ }
3566
+ else {
3567
+ log.info("Group addListUsers completed", { context: "addListUsers" });
3568
+ res.send(results);
3569
+ }
3570
+ });
3571
+ });
3572
+ router.get("/:id/:listId/getListUsers", auth.can("edit group"), (req, res) => {
3573
+ models.AcListUser.find({
3574
+ where: {
3575
+ ac_list_id: req.params.listId,
3576
+ },
3577
+ })
3578
+ .then((listUsers) => {
3579
+ res.send(listUsers);
3580
+ })
3581
+ .catch((error) => {
3582
+ log.error("Group getListUsers error", {
3583
+ context: "createCampaign",
3584
+ error,
3585
+ });
3586
+ res.sendStatus(500);
3587
+ });
3588
+ });
3589
+ router.post("/:id/marketingTrackingOpen", auth.can("view group"), (req, res) => {
3590
+ if (req.body &&
3591
+ req.body.originalQueryParameters &&
3592
+ req.body.originalQueryParameters.yu) {
3593
+ models.AcCampaign.updateCampaignAndUser(req.body.originalQueryParameters, "openCount", (error) => {
3594
+ if (error) {
3595
+ log.error("Group marketingTracking error", {
3596
+ context: "marketingTracking",
3597
+ params: req.body,
3598
+ error,
3599
+ });
3600
+ res.sendStatus(500);
3601
+ }
3602
+ else {
3603
+ log.info("Group marketingTracking marketing completed", {
3604
+ context: "marketingTracking",
3605
+ params: req.body,
3606
+ });
3607
+ res.sendStatus(200);
3608
+ }
3609
+ });
3610
+ }
3611
+ else {
3612
+ log.warn("Group marketingTracking no tracking parameters");
3613
+ res.sendStatus(200);
3614
+ }
3615
+ });
3616
+ router.post("/:id/triggerTrackingGoal", auth.can("view group"), (req, res) => {
3617
+ if (req.body &&
3618
+ req.body.originalQueryParameters &&
3619
+ req.body.originalQueryParameters.yu) {
3620
+ models.AcCampaign.updateCampaignAndUser(req.body.originalQueryParameters, "completeCount", (error) => {
3621
+ if (error) {
3622
+ log.error("Group triggerTrackingGoal error", {
3623
+ context: "marketingTracking",
3624
+ params: req.body,
3625
+ error,
3626
+ });
3627
+ res.sendStatus(500);
3628
+ }
3629
+ else {
3630
+ log.info("Group triggerTrackingGoal marketing completed", {
3631
+ context: "triggerTrackingGoal",
3632
+ params: req.body,
3633
+ });
3634
+ res.sendStatus(200);
3635
+ }
3636
+ });
3637
+ }
3638
+ else {
3639
+ models.Group.findOne({
3640
+ where: {
3641
+ id: req.params.id,
3642
+ },
3643
+ attributes: ["id", "configuration"],
3644
+ })
3645
+ .then(function (group) {
3646
+ if (group &&
3647
+ group.configuration &&
3648
+ group.configuration.externalGoalTriggerUrl) {
3649
+ log.info("Group triggerTrackingGoal starting", {
3650
+ context: "triggerTrackingGoal",
3651
+ params: req.body,
3652
+ group: group,
3653
+ });
3654
+ const options = {
3655
+ url: group.configuration.externalGoalTriggerUrl,
3656
+ qs: req.body,
3657
+ };
3658
+ request.get(options, (response, message) => {
3659
+ if ((response && response.errno) ||
3660
+ (message && message.statusCode !== 200)) {
3661
+ log.info("Group triggerTrackingGoal error", {
3662
+ context: "triggerTrackingGoal",
3663
+ response: response,
3664
+ message: message,
3665
+ params: req.body,
3666
+ group: group,
3667
+ });
3668
+ res.sendStatus(500);
3669
+ }
3670
+ else {
3671
+ log.info("Group triggerTrackingGoal completed", {
3672
+ context: "triggerTrackingGoal",
3673
+ params: req.body,
3674
+ group: group,
3675
+ });
3676
+ res.sendStatus(200);
3677
+ }
3678
+ });
3679
+ //
3680
+ }
3681
+ else {
3682
+ log.error("Error getting group for triggerTrackingGoal");
3683
+ res.sendStatus(404);
3684
+ }
3685
+ })
3686
+ .catch(function (error) {
3687
+ log.error("Error getting group for triggerTrackingGoal", { error });
3688
+ res.sendStatus(404);
3689
+ });
3690
+ }
3691
+ });
3692
+ router.put("/:id/:pointId/adminComment", auth.can("edit group"), function (req, res) {
3693
+ if (!req.body.content) {
3694
+ req.body.content = "";
3695
+ }
3696
+ models.Point.findOne({
3697
+ where: {
3698
+ id: req.params.pointId,
3699
+ },
3700
+ attributes: ["id", "public_data"],
3701
+ })
3702
+ .then(function (point) {
3703
+ if (point) {
3704
+ if (!point.public_data) {
3705
+ point.set("public_data", {});
3706
+ }
3707
+ if (!point.public_data.admin_comment) {
3708
+ point.set("public_data.admin_comment", {});
3709
+ }
3710
+ point.set("public_data.admin_comment", {
3711
+ text: req.body.content,
3712
+ userId: req.user.id,
3713
+ userName: req.user.name,
3714
+ createdAt: new Date(),
3715
+ });
3716
+ point
3717
+ .save()
3718
+ .then(() => {
3719
+ res.send({ content: req.body.content });
3720
+ })
3721
+ .catch(function (error) {
3722
+ log.error("Error adminComment", { error });
3723
+ res.sendStatus(500);
3724
+ });
3725
+ }
3726
+ else {
3727
+ res.sendStatus(404);
3728
+ }
3729
+ })
3730
+ .catch(function (error) {
3731
+ log.error("Error adminComment", { error });
3732
+ res.sendStatus(500);
3733
+ });
3734
+ });
3735
+ router.get("/:groupId/survey", auth.can("view group"), (req, res) => {
3736
+ models.Group.findOne({
3737
+ where: { id: req.params.groupId },
3738
+ order: [
3739
+ [{ model: models.Image, as: "GroupLogoImages" }, "created_at", "asc"],
3740
+ [{ model: models.Video, as: "GroupLogoVideos" }, "updated_at", "desc"],
3741
+ [
3742
+ { model: models.Video, as: "GroupLogoVideos" },
3743
+ { model: models.Image, as: "VideoImages" },
3744
+ "updated_at",
3745
+ "asc",
3746
+ ],
3747
+ ],
3748
+ attributes: ["id", "name", "configuration", "objectives"],
3749
+ include: [
3750
+ {
3751
+ model: models.Community,
3752
+ attributes: [
3753
+ "id",
3754
+ "theme_id",
3755
+ "name",
3756
+ "access",
3757
+ "google_analytics_code",
3758
+ "configuration",
3759
+ "only_admins_can_create_groups",
3760
+ ],
3761
+ include: [
3762
+ {
3763
+ model: models.Domain,
3764
+ attributes: ["id", "theme_id", "name"],
3765
+ },
3766
+ ],
3767
+ },
3768
+ {
3769
+ model: models.Image,
3770
+ as: "GroupLogoImages",
3771
+ attributes: models.Image.defaultAttributesPublic,
3772
+ required: false,
3773
+ },
3774
+ {
3775
+ model: models.Video,
3776
+ as: "GroupLogoVideos",
3777
+ attributes: ["id", "formats", "viewable", "public_meta"],
3778
+ required: false,
3779
+ include: [
3780
+ {
3781
+ model: models.Image,
3782
+ as: "VideoImages",
3783
+ attributes: ["formats", "updated_at"],
3784
+ required: false,
3785
+ },
3786
+ ],
3787
+ },
3788
+ ],
3789
+ })
3790
+ .then(function (group) {
3791
+ if (group) {
3792
+ log.info("Survey Group Viewed", {
3793
+ groupId: group.id,
3794
+ context: "view",
3795
+ userId: req.user ? req.user.id : -1,
3796
+ });
3797
+ res.send({ surveyGroup: group });
3798
+ }
3799
+ else {
3800
+ res.send({ error: "notFound" });
3801
+ }
3802
+ })
3803
+ .catch(function (error) {
3804
+ sendGroupOrError(res, null, "view survey", req.user, error);
3805
+ });
3806
+ });
3807
+ router.post("/:groupId/survey", auth.can("view group"), (req, res) => {
3808
+ let surveyGroup;
3809
+ let loggedInUser = req.user;
3810
+ async.series([
3811
+ (seriesCallback) => {
3812
+ models.Group.findOne({
3813
+ where: { id: req.params.groupId },
3814
+ attributes: ["id", "configuration"],
3815
+ })
3816
+ .then((group) => {
3817
+ if (group) {
3818
+ surveyGroup = group;
3819
+ seriesCallback();
3820
+ }
3821
+ else {
3822
+ seriesCallback("Group not found");
3823
+ }
3824
+ })
3825
+ .catch((error) => {
3826
+ seriesCallback(error);
3827
+ });
3828
+ },
3829
+ (seriesCallback) => {
3830
+ if (loggedInUser) {
3831
+ seriesCallback();
3832
+ }
3833
+ else {
3834
+ const anonSurveyEmail = "survey_group_" + surveyGroup.id + "_user_anonymous@citizens.is";
3835
+ models.User.findOne({
3836
+ where: {
3837
+ email: anonSurveyEmail,
3838
+ },
3839
+ })
3840
+ .then(function (existingUser) {
3841
+ if (existingUser) {
3842
+ loggedInUser = existingUser;
3843
+ seriesCallback();
3844
+ }
3845
+ else {
3846
+ var user = models.User.build({
3847
+ email: anonSurveyEmail,
3848
+ name: "Anonymous Survey User",
3849
+ notifications_settings: models.AcNotification.anonymousNotificationSettings,
3850
+ status: "active",
3851
+ });
3852
+ user.set("profile_data", {});
3853
+ user.set("profile_data.isAnonymousUser", true);
3854
+ user
3855
+ .save()
3856
+ .then(() => {
3857
+ loggedInUser = user;
3858
+ seriesCallback();
3859
+ })
3860
+ .catch((error) => {
3861
+ seriesCallback(error);
3862
+ });
3863
+ }
3864
+ })
3865
+ .catch((error) => {
3866
+ seriesCallback(error);
3867
+ });
3868
+ }
3869
+ },
3870
+ (seriesCallback) => {
3871
+ const post = models.Post.build({
3872
+ name: "Survey Response -" +
3873
+ moment(new Date()).format("DD/MM/YYYY hh:mm:ss"),
3874
+ description: "",
3875
+ group_id: surveyGroup.id,
3876
+ cover_media_type: "none",
3877
+ user_id: loggedInUser.id,
3878
+ status: "blocked",
3879
+ counter_endorsements_up: 0,
3880
+ content_type: models.Post.CONTENT_SURVEY,
3881
+ user_agent: req.useragent.source,
3882
+ ip_address: req.clientIp,
3883
+ });
3884
+ post.set("public_data", {});
3885
+ if (req.body.structuredAnswers) {
3886
+ post.set("public_data.structuredAnswersJson", req.body.structuredAnswers);
3887
+ }
3888
+ post
3889
+ .save()
3890
+ .then(() => {
3891
+ log.info("Survey Post Created", {
3892
+ postId: post.id,
3893
+ context: "create",
3894
+ });
3895
+ post.updateAllExternalCounters(req, "up", "counter_posts", function () {
3896
+ seriesCallback();
3897
+ });
3898
+ })
3899
+ .catch((error) => {
3900
+ seriesCallback(error);
3901
+ });
3902
+ },
3903
+ ], (error) => {
3904
+ if (error) {
3905
+ sendGroupOrError(res, null, "post survey", req.user, error);
3906
+ }
3907
+ else {
3908
+ res.sendStatus(200);
3909
+ }
3910
+ });
3911
+ });
3912
+ // WORD CLOUD
3913
+ router.get("/:id/wordcloud", auth.can("edit group"), function (req, res) {
3914
+ getFromAnalyticsApi(req, "wordclouds", "group", req.params.id, function (error, content) {
3915
+ sendBackAnalyticsResultsOrError(req, res, error, content);
3916
+ });
3917
+ });
3918
+ // SIMILARITIES
3919
+ router.get("/:id/similarities_weights", auth.can("edit group"), function (req, res) {
3920
+ getFromAnalyticsApi(req, "similarities_weights", "group", req.params.id, function (error, content) {
3921
+ sendBackAnalyticsResultsOrError(req, res, error ? error : content.body ? null : "noBody", getParsedSimilaritiesContent(content));
3922
+ });
3923
+ });
3924
+ // STATS
3925
+ router.get("/:id/stats_posts", auth.can("edit group"), function (req, res) {
3926
+ countModelRowsByTimePeriod(req, "stats_posts_" + req.params.id + "_group", models.Post, {}, getGroupIncludes(req.params.id), (error, results) => {
3927
+ sendBackAnalyticsResultsOrError(req, res, error, results);
3928
+ });
3929
+ });
3930
+ router.get("/:id/stats_points", auth.can("edit group"), function (req, res) {
3931
+ countModelRowsByTimePeriod(req, "stats_points_" + req.params.id + "_group", models.Point, {}, getPointGroupIncludes(req.params.id), (error, results) => {
3932
+ sendBackAnalyticsResultsOrError(req, res, error, results);
3933
+ });
3934
+ });
3935
+ router.get("/:id/stats_votes", auth.can("edit group"), function (req, res) {
3936
+ countModelRowsByTimePeriod(req, "stats_votes_" + req.params.id + "_group", models.AcActivity, {
3937
+ type: {
3938
+ $in: [
3939
+ "activity.post.opposition.new",
3940
+ "activity.post.endorsement.new",
3941
+ "activity.point.helpful.new",
3942
+ "activity.point.unhelpful.new",
3943
+ ],
3944
+ },
3945
+ }, getGroupIncludes(req.params.id), (error, results) => {
3946
+ sendBackAnalyticsResultsOrError(req, res, error, results);
3947
+ });
3948
+ });
3949
+ router.get("/:id/get_translation_texts", auth.can("edit group"), function (req, res) {
3950
+ getTranslatedTextsForGroup(req.query.targetLocale, req.params.id, (results, error) => {
3951
+ if (error) {
3952
+ log.error("Error in getting translated texts", { error });
3953
+ res.sendStatus(500);
3954
+ }
3955
+ else {
3956
+ res.send(results);
3957
+ }
3958
+ });
3959
+ });
3960
+ router.put("/:id/update_translation", auth.can("edit group"), function (req, res) {
3961
+ updateTranslationForGroup(req.params.id, req.body, (results, error) => {
3962
+ if (error) {
3963
+ log.error("Error in updating translation", { error });
3964
+ res.sendStatus(500);
3965
+ }
3966
+ else {
3967
+ res.send(results);
3968
+ }
3969
+ });
3970
+ });
3971
+ router.put("/:id/update_structured_translations", auth.can("edit group"), function (req, res) {
3972
+ let textType;
3973
+ if (req.body.type == "registration") {
3974
+ textType = "GroupRegQuestions";
3975
+ }
3976
+ else if (req.body.type.indexOf("PostAnswer") > -1) {
3977
+ textType = "PostAnswer";
3978
+ }
3979
+ else {
3980
+ textType = "GroupQuestions";
3981
+ }
3982
+ if (textType === "PostAnswer") {
3983
+ let contentHash = req.body.type.split("-")[3];
3984
+ let postId = req.body.type.split("-")[1];
3985
+ updateAnswerTranslation(postId, textType, req.body.targetLocale, req.body.translations, contentHash, (results, error) => {
3986
+ if (error) {
3987
+ log.error("Error in updating survey translation", { error });
3988
+ res.sendStatus(500);
3989
+ }
3990
+ else {
3991
+ res.send(results);
3992
+ }
3993
+ });
3994
+ }
3995
+ else {
3996
+ updateSurveyTranslation(req.params.id, textType, req.body.targetLocale, req.body.translations, req.body.questions, (results, error) => {
3997
+ if (error) {
3998
+ log.error("Error in updating survey translation", { error });
3999
+ res.sendStatus(500);
4000
+ }
4001
+ else {
4002
+ res.send(results);
4003
+ }
4004
+ });
4005
+ }
4006
+ });
4007
+ var upload = multer({
4008
+ storage: s3multer({
4009
+ dirname: "attachments",
4010
+ s3: s3,
4011
+ bucket: process.env.S3_BUCKET,
4012
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
4013
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
4014
+ endpoint: process.env.S3_ENDPOINT || null,
4015
+ acl: "public-read",
4016
+ contentType: s3multer.AUTO_CONTENT_TYPE,
4017
+ region: process.env.S3_REGION || (process.env.S3_ENDPOINT ? null : "us-east-1"),
4018
+ key: function (req, file, cb) {
4019
+ cb(null, Date.now() + "_" + file.originalname);
4020
+ },
4021
+ }),
4022
+ });
4023
+ //TODO: Old remove only here for cached serviceworker clients
4024
+ router.post("/:id/upload_document", auth.can("add to group"), upload.single("file"), function (req, res) {
4025
+ res.send({ filename: req.file.originalname, url: req.file.location });
4026
+ });
4027
+ var uploadDox = multer({});
4028
+ router.put("/:id/convert_docx_survey_to_json", uploadDox.single("file"), function (req, res) {
4029
+ const formData = {
4030
+ file: {
4031
+ value: req.file.buffer,
4032
+ options: {
4033
+ filename: req.file.originalname,
4034
+ },
4035
+ },
4036
+ };
4037
+ convertDocxSurveyToJson(formData, (error, results) => {
4038
+ if (error) {
4039
+ log.error("Error in converting docx to joson", { error });
4040
+ res.sendStatus(500);
4041
+ }
4042
+ else {
4043
+ if (results.body) {
4044
+ res.send({ jsonContent: JSON.parse(results.body) });
4045
+ }
4046
+ else {
4047
+ res.sendStatus(500);
4048
+ }
4049
+ }
4050
+ });
4051
+ });
4052
+ router.put("/:groupId/plausibleStatsProxy", auth.can("edit group marketing"), async (req, res) => {
4053
+ try {
4054
+ const plausibleData = await plausibleStatsProxy(req.body.plausibleUrl, {
4055
+ groupId: req.params.groupId,
4056
+ });
4057
+ res.send(plausibleData);
4058
+ }
4059
+ catch (error) {
4060
+ log.error("Could not get plausibleStatsProxy", {
4061
+ err: error,
4062
+ context: "getPlausibleSeries",
4063
+ user: toJson(req.user.simple()),
4064
+ });
4065
+ res.sendStatus(500);
4066
+ }
4067
+ });
4068
+ router.get("/:domainId/getTemplates", auth.can("view domain"), async (req, res) => {
4069
+ try {
4070
+ const userId = req.user.id;
4071
+ const domainId = req.params.domainId;
4072
+ const templateGroups = await models.Group.findAll({
4073
+ where: {
4074
+ [Op.and]: [
4075
+ { "configuration.useAsTemplate": true },
4076
+ {
4077
+ [Op.or]: [
4078
+ // public templates
4079
+ { access: models.Group.ACCESS_PUBLIC },
4080
+ // templates where user is an admin
4081
+ literal(`"GroupAdmins"."id" IS NOT NULL`)
4082
+ ]
4083
+ }
4084
+ ]
4085
+ },
4086
+ include: [
4087
+ {
4088
+ model: models.User,
4089
+ as: "GroupAdmins",
4090
+ attributes: [],
4091
+ through: { attributes: [] },
4092
+ where: { id: userId },
4093
+ required: false,
4094
+ },
4095
+ {
4096
+ model: models.Community,
4097
+ as: "Community",
4098
+ attributes: ["id", "name"],
4099
+ required: true,
4100
+ include: [
4101
+ {
4102
+ model: models.Domain,
4103
+ attributes: ["id", "name"],
4104
+ required: true,
4105
+ where: { id: domainId }
4106
+ },
4107
+ ],
4108
+ }
4109
+ ],
4110
+ attributes: ["id", "name"],
4111
+ distinct: true,
4112
+ order: [["name", "ASC"]]
4113
+ });
4114
+ res.send(templateGroups || []);
4115
+ }
4116
+ catch (error) {
4117
+ log.error("Could not get template groups", {
4118
+ err: error,
4119
+ context: "getTemplates",
4120
+ user: toJson(req.user.simple()),
4121
+ });
4122
+ res.sendStatus(500);
4123
+ }
4124
+ });
4125
+ router.get("/:groupId/:type/getPlausibleSeries", auth.can("edit group marketing"), async (req, res) => {
4126
+ // Example: "timeseries?site_id=your-priorities&period=7d";
4127
+ try {
4128
+ const questionMarkIndex = req.url.indexOf("?");
4129
+ const queryString = req.url.substr(questionMarkIndex + 1);
4130
+ const siteId = process.env.PLAUSIBLE_SITE_NAME;
4131
+ const type = req.params.type.replace("realtime-visitors", "realtime/visitors");
4132
+ const plausibleString = `${type}?${queryString}&site_id=${siteId}`;
4133
+ const plausibleData = await getPlausibleStats(plausibleString);
4134
+ log.info("GOT DATA");
4135
+ log.info(plausibleData);
4136
+ res.send(plausibleData);
4137
+ }
4138
+ catch (error) {
4139
+ log.error("Could not get getPlausibleSeries", {
4140
+ err: error,
4141
+ context: "getPlausibleSeries",
4142
+ user: toJson(req.user.simple()),
4143
+ });
4144
+ res.sendStatus(500);
4145
+ }
4146
+ });
4147
+ router.get("/:groupId/get_campaigns", auth.can("edit group marketing"), async (req, res) => {
4148
+ try {
4149
+ const campaigns = await models.Campaign.findAll({
4150
+ where: {
4151
+ group_id: req.params.groupId,
4152
+ },
4153
+ order: [["created_at", "desc"]],
4154
+ attributes: ["id", "configuration"],
4155
+ });
4156
+ res.send(campaigns);
4157
+ }
4158
+ catch (error) {
4159
+ log.error("Could not get campaigns", {
4160
+ err: error,
4161
+ context: "get_campaigns",
4162
+ user: toJson(req.user.simple()),
4163
+ });
4164
+ res.sendStatus(500);
4165
+ }
4166
+ });
4167
+ router.post("/:groupId/create_campaign", auth.can("edit group marketing"), async (req, res) => {
4168
+ try {
4169
+ const campaign = models.Campaign.build({
4170
+ group_id: req.params.groupId,
4171
+ configuration: req.body.configuration,
4172
+ user_id: req.user.id,
4173
+ });
4174
+ await campaign.save();
4175
+ //TODO: Toxicity check
4176
+ res.send(campaign);
4177
+ }
4178
+ catch (error) {
4179
+ log.error("Could not create_campaign campaigns", {
4180
+ err: error,
4181
+ context: "create_campaign",
4182
+ user: toJson(req.user.simple()),
4183
+ });
4184
+ res.sendStatus(500);
4185
+ }
4186
+ });
4187
+ router.put("/:groupId/:campaignId/update_campaign", auth.can("edit group marketing"), async (req, res) => {
4188
+ try {
4189
+ const campaign = await models.Campaign.findOne({
4190
+ where: {
4191
+ id: req.params.campaignId,
4192
+ group_id: req.params.groupId,
4193
+ },
4194
+ attributes: ["id", "configuration"],
4195
+ });
4196
+ campaign.configuration = req.body.configuration;
4197
+ await campaign.save();
4198
+ //TODO: Toxicity check
4199
+ res.send(campaign);
4200
+ }
4201
+ catch (error) {
4202
+ log.error("Could not create_campaign campaigns", {
4203
+ err: error,
4204
+ context: "create_campaign",
4205
+ user: toJson(req.user.simple()),
4206
+ });
4207
+ res.sendStatus(500);
4208
+ }
4209
+ });
4210
+ router.delete("/:groupId/:campaignId/delete_campaign", auth.can("edit group marketing"), async (req, res) => {
4211
+ try {
4212
+ const campaign = await models.Campaign.findOne({
4213
+ where: {
4214
+ id: req.params.campaignId,
4215
+ group_id: req.params.groupId,
4216
+ },
4217
+ attributes: ["id"],
4218
+ });
4219
+ campaign.deleted = true;
4220
+ await campaign.save();
4221
+ res.sendStatus(200);
4222
+ }
4223
+ catch (error) {
4224
+ log.error("Could not delete_campaign campaigns", {
4225
+ err: error,
4226
+ context: "delete_campaign",
4227
+ user: toJson(req.user.simple()),
4228
+ });
4229
+ res.sendStatus(500);
4230
+ }
4231
+ });
4232
+ router.get("/:groupId/get_posts_with_public_private", auth.can("view group"), async (req, res) => {
4233
+ try {
4234
+ const posts = await models.Post.findAll({
4235
+ where: {
4236
+ group_id: req.params.groupId,
4237
+ [Sequelize.Op.and]: [
4238
+ Sequelize.literal(`"data"->'publicPrivateData' IS NOT NULL`),
4239
+ ],
4240
+ },
4241
+ order: [["created_at", "desc"]],
4242
+ attributes: [
4243
+ "id",
4244
+ [
4245
+ models.sequelize.literal("\"data\"->'publicPrivateData'"),
4246
+ "publicPrivateData",
4247
+ ],
4248
+ ],
4249
+ });
4250
+ res.send(posts);
4251
+ }
4252
+ catch (error) {
4253
+ log.error("Could not get get_posts_with_public_private", {
4254
+ err: error,
4255
+ context: "get_campaigns",
4256
+ user: toJson(req.user.simple()),
4257
+ });
4258
+ res.sendStatus(500);
4259
+ }
4260
+ });
4261
+ router.get("/:groupId/:pointId/get_parent_point", auth.can("view group"), async (req, res) => {
4262
+ try {
4263
+ const point = await models.Point.findOne({
4264
+ where: {
4265
+ group_id: req.params.groupId,
4266
+ id: req.params.pointId,
4267
+ },
4268
+ order: [
4269
+ [models.PointRevision, "created_at", "asc"],
4270
+ [
4271
+ models.User,
4272
+ { model: models.Image, as: "UserProfileImages" },
4273
+ "created_at",
4274
+ "asc",
4275
+ ],
4276
+ ],
4277
+ attributes: ["id", "content"],
4278
+ include: [
4279
+ {
4280
+ model: models.PointRevision,
4281
+ required: false,
4282
+ },
4283
+ {
4284
+ model: models.User,
4285
+ required: true,
4286
+ attributes: models.User.defaultAttributesWithSocialMediaPublic,
4287
+ include: [
4288
+ {
4289
+ model: models.Image,
4290
+ as: "UserProfileImages",
4291
+ attributes: ["id", "formats", "updated_at"],
4292
+ required: false,
4293
+ },
4294
+ ],
4295
+ },
4296
+ ],
4297
+ });
4298
+ res.send(point);
4299
+ }
4300
+ catch (error) {
4301
+ log.error("Could not get get_parent_point", {
4302
+ err: error,
4303
+ context: "get_campaigns",
4304
+ user: toJson(req.user.simple()),
4305
+ });
4306
+ res.sendStatus(500);
4307
+ }
4308
+ });
4309
+ module.exports = router;