Ver código fonte

Merge branch 'nztqa-improve-notify' into devel

Add Feature: Outgoing Webhooks. Thanks to nztqa !
Lauri Ojansivu 8 anos atrás
pai
commit
afa5ec360f

+ 3 - 1
.eslintrc.json

@@ -127,6 +127,8 @@
     "InvitationCodes": true,
     "InvitationCodes": true,
     "Winston":true,
     "Winston":true,
     "JsonRoutes": true,
     "JsonRoutes": true,
-    "Authentication": true
+    "Authentication": true,
+    "Integrations": true,
+    "HTTP": true
   }
   }
 }
 }

+ 1 - 0
.meteor/packages

@@ -57,6 +57,7 @@ mquandalle:moment
 ongoworks:speakingurl
 ongoworks:speakingurl
 raix:handlebar-helpers
 raix:handlebar-helpers
 tap:i18n
 tap:i18n
+http
 
 
 # UI components
 # UI components
 blaze
 blaze

+ 2 - 1
CHANGELOG.md

@@ -7,7 +7,8 @@ This release adds the following new features:
 * [When finished input of checklist item, open new checklist
 * [When finished input of checklist item, open new checklist
   item](https://github.com/wekan/wekan/pull/1099);
   item](https://github.com/wekan/wekan/pull/1099);
 * [Improve UI design of checklist items](https://github.com/wekan/wekan/pull/1108);
 * [Improve UI design of checklist items](https://github.com/wekan/wekan/pull/1108);
-* [Import Wekan board](https://github.com/wekan/wekan/pull/1117).
+* [Import Wekan board](https://github.com/wekan/wekan/pull/1117);
+* [Outgoing Webhooks](https://github.com/wekan/wekan/pull/1119).
 
 
 and fixes the following bugs:
 and fixes the following bugs:
 
 

+ 11 - 0
client/components/boards/boardHeader.jade

@@ -112,6 +112,7 @@ template(name="boardMenuPopup")
       ul.pop-over-list
       ul.pop-over-list
         li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
         li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}}
         li: a.js-archive-board {{_ 'archive-board'}}
         li: a.js-archive-board {{_ 'archive-board'}}
+        li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}}
 
 
 template(name="boardVisibilityList")
 template(name="boardVisibilityList")
   ul.pop-over-list
   ul.pop-over-list
@@ -213,3 +214,13 @@ template(name="boardChangeTitlePopup")
 template(name="archiveBoardPopup")
 template(name="archiveBoardPopup")
   p {{_ 'close-board-pop'}}
   p {{_ 'close-board-pop'}}
   button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
   button.js-confirm.negate.full(type="submit") {{_ 'archive'}}
+
+template(name="outgoingWebhooksPopup")
+  form
+    label
+      | URL
+      if integration.enabled
+        input.js-outgoing-webhooks-url(type="text" value=integration.url autofocus)
+      else
+        input.js-outgoing-webhooks-url(type="text" autofocus)
+    input.primary.wide(type="submit" value="{{_ 'save'}}")

+ 43 - 0
client/components/boards/boardHeader.js

@@ -13,6 +13,7 @@ Template.boardMenuPopup.events({
     // confirm that the board was successfully archived.
     // confirm that the board was successfully archived.
     FlowRouter.go('home');
     FlowRouter.go('home');
   }),
   }),
+  'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
 });
 });
 
 
 Template.boardMenuPopup.helpers({
 Template.boardMenuPopup.helpers({
@@ -234,3 +235,45 @@ BlazeComponent.extendComponent({
     }];
     }];
   },
   },
 }).register('boardChangeWatchPopup');
 }).register('boardChangeWatchPopup');
+
+BlazeComponent.extendComponent({
+  integration() {
+    const boardId = Session.get('currentBoard');
+    return Integrations.findOne({ boardId: `${boardId}` });
+  },
+
+  events() {
+    return [{
+      'submit'(evt) {
+        evt.preventDefault();
+        const url = this.find('.js-outgoing-webhooks-url').value.trim();
+        const boardId = Session.get('currentBoard');
+        const integration = this.integration();
+        if (integration) {
+          if (url) {
+            Integrations.update(integration._id, {
+              $set: {
+                enabled: true,
+                url: `${url}`,
+              },
+            });
+          } else {
+            Integrations.update(integration._id, {
+              $set: {
+                enabled: false,
+              },
+            });
+          }
+        } else if (url) {
+          Integrations.insert({
+            enabled: true,
+            type: 'outgoing-webhooks',
+            url: `${url}`,
+            boardId: `${boardId}`,
+          });
+        }
+        Popup.close();
+      },
+    }];
+  },
+}).register('outgoingWebhooksPopup');

+ 3 - 1
i18n/ar.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/br.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/ca.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ t'ha convidat",
     "email-invite-register-subject": "__inviter__ t'ha convidat",
     "email-invite-register-text": " __user__,\n\n __inviter__  us ha convidat a col·laborar a Wekan.\n\n Clicau l'enllaç següent per acceptar l'invitació:\n __url__\n\n El vostre codi d'invitació és: __icode__\n\n Gràcies",
     "email-invite-register-text": " __user__,\n\n __inviter__  us ha convidat a col·laborar a Wekan.\n\n Clicau l'enllaç següent per acceptar l'invitació:\n __url__\n\n El vostre codi d'invitació és: __icode__\n\n Gràcies",
     "error-invitation-code-not-exist": "El codi d'invitació no existeix",
     "error-invitation-code-not-exist": "El codi d'invitació no existeix",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/cs.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ odeslal pozvánku",
     "email-invite-register-subject": "__inviter__ odeslal pozvánku",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/de.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ hat Ihnen eine Einladung geschickt",
     "email-invite-register-subject": "__inviter__ hat Ihnen eine Einladung geschickt",
     "email-invite-register-text": "Hallo __user__,\n\n__inviter__ hat Sie für Ihre Zusammenarbeit zu Wekan eingeladen.\n\nBitte klicken Sie auf folgenden Link:\n__url__\n\nIhr Einladungscode lautet: __icode__\n\nDanke.",
     "email-invite-register-text": "Hallo __user__,\n\n__inviter__ hat Sie für Ihre Zusammenarbeit zu Wekan eingeladen.\n\nBitte klicken Sie auf folgenden Link:\n__url__\n\nIhr Einladungscode lautet: __icode__\n\nDanke.",
     "error-invitation-code-not-exist": "Ungültiger Einladungscode",
     "error-invitation-code-not-exist": "Ungültiger Einladungscode",
-    "error-notAuthorized": "Sie sind nicht berechtigt diese Seite zu sehen."
+    "error-notAuthorized": "Sie sind nicht berechtigt diese Seite zu sehen.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/en-GB.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaboration.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaboration.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorised to view this page."
+    "error-notAuthorized": "You are not authorised to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/en.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/eo.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/es.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/eu.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ erabiltzaileak gonbidapen bat bidali dizu",
     "email-invite-register-subject": "__inviter__ erabiltzaileak gonbidapen bat bidali dizu",
     "email-invite-register-text": "Kaixo __user__,\n\n__inviter__ erabiltzaileak Wekanera gonbidatu zaitu elkar-lanean aritzeko.\n\nJarraitu mesedez lotura hau:\n__url__\n\nZure gonbidapen kodea hau da: __icode__\n\nEskerrik asko.",
     "email-invite-register-text": "Kaixo __user__,\n\n__inviter__ erabiltzaileak Wekanera gonbidatu zaitu elkar-lanean aritzeko.\n\nJarraitu mesedez lotura hau:\n__url__\n\nZure gonbidapen kodea hau da: __icode__\n\nEskerrik asko.",
     "error-invitation-code-not-exist": "Gonbidapen kodea ez da existitzen",
     "error-invitation-code-not-exist": "Gonbidapen kodea ez da existitzen",
-    "error-notAuthorized": "Ez duzu orri hau ikusteko baimenik."
+    "error-notAuthorized": "Ez duzu orri hau ikusteko baimenik.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/fa.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ برای شما دعوت نامه ارسال کرده است",
     "email-invite-register-subject": "__inviter__ برای شما دعوت نامه ارسال کرده است",
     "email-invite-register-text": "__User__ عزیز \nکاربر __inviter__ شما را به عضویت در Wekan برای همکاری دعوت کرده است.\nلطفا لینک زیر را دنبال کنید،\n __url__\nکد دعوت شما __icode__ می باشد.\n باتشکر",
     "email-invite-register-text": "__User__ عزیز \nکاربر __inviter__ شما را به عضویت در Wekan برای همکاری دعوت کرده است.\nلطفا لینک زیر را دنبال کنید،\n __url__\nکد دعوت شما __icode__ می باشد.\n باتشکر",
     "error-invitation-code-not-exist": "چنین کد دعوتی یافت نشد",
     "error-invitation-code-not-exist": "چنین کد دعوتی یافت نشد",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/fi.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ lähetti sinulle kutsun",
     "email-invite-register-subject": "__inviter__ lähetti sinulle kutsun",
     "email-invite-register-text": "Hei __user__,\n\n__inviter__ kutsuu sinut mukaan Wekan ohjelman käyttöön.\n\nOle hyvä ja seuraa allaolevaa linkkiä:\n__url__\n\nJa kutsukoodisi on: __icode__\n\nKiitos.",
     "email-invite-register-text": "Hei __user__,\n\n__inviter__ kutsuu sinut mukaan Wekan ohjelman käyttöön.\n\nOle hyvä ja seuraa allaolevaa linkkiä:\n__url__\n\nJa kutsukoodisi on: __icode__\n\nKiitos.",
     "error-invitation-code-not-exist": "Kutsukoodi ei ole olemassa",
     "error-invitation-code-not-exist": "Kutsukoodi ei ole olemassa",
-    "error-notAuthorized": "Sinulla ei ole oikeutta tarkastella tätä sivua."
+    "error-notAuthorized": "Sinulla ei ole oikeutta tarkastella tätä sivua.",
+    "outgoing-webhooks": "Lähtevät Webkoukut",
+    "outgoingWebhooksPopup-title": "Lähtevät Webkoukut"
 }
 }

+ 3 - 1
i18n/fr.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ vous a envoyé une invitation",
     "email-invite-register-subject": "__inviter__ vous a envoyé une invitation",
     "email-invite-register-text": "Cher __user__,\n\n__inviter__ vous invite à le rejoindre sur Wekan pour collaborer.\n\nVeuillez suivre le lien ci-dessous :\n__url__\n\nVotre code d'invitation est : __icode__\n\nMerci.",
     "email-invite-register-text": "Cher __user__,\n\n__inviter__ vous invite à le rejoindre sur Wekan pour collaborer.\n\nVeuillez suivre le lien ci-dessous :\n__url__\n\nVotre code d'invitation est : __icode__\n\nMerci.",
     "error-invitation-code-not-exist": "Ce code d'invitation n'existe pas.",
     "error-invitation-code-not-exist": "Ce code d'invitation n'existe pas.",
-    "error-notAuthorized": "Vous n'êtes pas autorisé à accéder à cette page."
+    "error-notAuthorized": "Vous n'êtes pas autorisé à accéder à cette page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/he.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "נשלחה אליך הזמנה מאת __inviter__",
     "email-invite-register-subject": "נשלחה אליך הזמנה מאת __inviter__",
     "email-invite-register-text": "__user__ יקר,\n\nקיבלת הזמנה מאת __inviter__ לשתף פעולה ב־Wekan.\n\nנא ללחוץ על הקישור:\n__url__\n\nקוד ההזמנה שלך הוא: __icode__\n\nתודה.",
     "email-invite-register-text": "__user__ יקר,\n\nקיבלת הזמנה מאת __inviter__ לשתף פעולה ב־Wekan.\n\nנא ללחוץ על הקישור:\n__url__\n\nקוד ההזמנה שלך הוא: __icode__\n\nתודה.",
     "error-invitation-code-not-exist": "קוד ההזמנה אינו קיים",
     "error-invitation-code-not-exist": "קוד ההזמנה אינו קיים",
-    "error-notAuthorized": "אין לך הרשאה לצפות בעמוד זה."
+    "error-notAuthorized": "אין לך הרשאה לצפות בעמוד זה.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/hu.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ küldött neked egy meghívót",
     "email-invite-register-subject": "__inviter__ küldött neked egy meghívót",
     "email-invite-register-text": "Kedves __user__,\n\n__inviter__ meghívott közreműködésre a Wekanba.\n\nKérlek kövesd az alábbi linket:\n__url__\n\nA meghívókódod: __icode__\n\nKöszönjük.",
     "email-invite-register-text": "Kedves __user__,\n\n__inviter__ meghívott közreműködésre a Wekanba.\n\nKérlek kövesd az alábbi linket:\n__url__\n\nA meghívókódod: __icode__\n\nKöszönjük.",
     "error-invitation-code-not-exist": "A meghívó kódja nem érvényes",
     "error-invitation-code-not-exist": "A meghívó kódja nem érvényes",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/id.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ mengirim undangan ke Anda",
     "email-invite-register-subject": "__inviter__ mengirim undangan ke Anda",
     "email-invite-register-text": "Halo __user__,\n\n__inviter__ mengundang Anda untuk berkolaborasi menggunakan Wekan.\n\nMohon ikuti tautan berikut:\n__url__\n\nDan kode undangan Anda adalah: __icode__\n\nTerima kasih.",
     "email-invite-register-text": "Halo __user__,\n\n__inviter__ mengundang Anda untuk berkolaborasi menggunakan Wekan.\n\nMohon ikuti tautan berikut:\n__url__\n\nDan kode undangan Anda adalah: __icode__\n\nTerima kasih.",
     "error-invitation-code-not-exist": "Kode undangan tidak ada",
     "error-invitation-code-not-exist": "Kode undangan tidak ada",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/it.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ ti ha inviato un invito",
     "email-invite-register-subject": "__inviter__ ti ha inviato un invito",
     "email-invite-register-text": "Gentile __user__,\n\n__inviter__ ti ha invitato su Wekan per collaborare.\n\nPer favore segui il link qui sotto:\n__url__\n\nIl tuo codice d'invito è: __icode__\n\nGrazie.",
     "email-invite-register-text": "Gentile __user__,\n\n__inviter__ ti ha invitato su Wekan per collaborare.\n\nPer favore segui il link qui sotto:\n__url__\n\nIl tuo codice d'invito è: __icode__\n\nGrazie.",
     "error-invitation-code-not-exist": "Il codice d'invito non esiste",
     "error-invitation-code-not-exist": "Il codice d'invito non esiste",
-    "error-notAuthorized": "Non sei autorizzato ad accedere a questa pagina."
+    "error-notAuthorized": "Non sei autorizzato ad accedere a questa pagina.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/ja.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "招待コードが存在しません",
     "error-invitation-code-not-exist": "招待コードが存在しません",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/ko.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "\"__inviter__ 님이 당신에게 초대장을 보냈습니다.",
     "email-invite-register-subject": "\"__inviter__ 님이 당신에게 초대장을 보냈습니다.",
     "email-invite-register-text": "\"__user__ 님, \n\n__inviter__ 님이 Wekan 보드에 협업을 위하여 초대합니다.\n\n아래 링크를 클릭해주세요 : \n__url__\n\n그리고 초대 코드는 __icode__ 입니다.\n\n감사합니다.",
     "email-invite-register-text": "\"__user__ 님, \n\n__inviter__ 님이 Wekan 보드에 협업을 위하여 초대합니다.\n\n아래 링크를 클릭해주세요 : \n__url__\n\n그리고 초대 코드는 __icode__ 입니다.\n\n감사합니다.",
     "error-invitation-code-not-exist": "초대 코드가 존재하지 않습니다.",
     "error-invitation-code-not-exist": "초대 코드가 존재하지 않습니다.",
-    "error-notAuthorized": "이 페이지를 볼 수있는 권한이 없습니다."
+    "error-notAuthorized": "이 페이지를 볼 수있는 권한이 없습니다.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/nb.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/pl.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ wysłał Ci zaproszenie",
     "email-invite-register-subject": "__inviter__ wysłał Ci zaproszenie",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/pt-BR.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ lhe enviou um convite",
     "email-invite-register-subject": "__inviter__ lhe enviou um convite",
     "email-invite-register-text": "Caro __user__,\n\n__inviter__ convidou você para colaborar no Wekan.\n\nPor favor, vá no link abaixo:\n__url__\n\nE seu código de convite é: __icode__\n\nObrigado.",
     "email-invite-register-text": "Caro __user__,\n\n__inviter__ convidou você para colaborar no Wekan.\n\nPor favor, vá no link abaixo:\n__url__\n\nE seu código de convite é: __icode__\n\nObrigado.",
     "error-invitation-code-not-exist": "O código do convite não existe",
     "error-invitation-code-not-exist": "O código do convite não existe",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/ro.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/ru.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ прислал вам приглашение",
     "email-invite-register-subject": "__inviter__ прислал вам приглашение",
     "email-invite-register-text": "Уважаемый __user__,\n\n__inviter__ приглашает вас в Wekan для сотрудничества.\n\nПожалуйста, проследуйте по ссылке:\n__url__\n\nВаш код приглашения: __icode__\n\nСпасибо.",
     "email-invite-register-text": "Уважаемый __user__,\n\n__inviter__ приглашает вас в Wekan для сотрудничества.\n\nПожалуйста, проследуйте по ссылке:\n__url__\n\nВаш код приглашения: __icode__\n\nСпасибо.",
     "error-invitation-code-not-exist": "Код приглашения не существует",
     "error-invitation-code-not-exist": "Код приглашения не существует",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/sr.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/sv.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ skickade dig en inbjudan",
     "email-invite-register-subject": "__inviter__ skickade dig en inbjudan",
     "email-invite-register-text": "Bästa __user__,\n\n__inviter__ inbjuder dig till Wekan för samarbeten.\n\nVänligen följ länken nedan:\n__url__\n\nOch din inbjudningskod är: __icode__\n\nTack.",
     "email-invite-register-text": "Bästa __user__,\n\n__inviter__ inbjuder dig till Wekan för samarbeten.\n\nVänligen följ länken nedan:\n__url__\n\nOch din inbjudningskod är: __icode__\n\nTack.",
     "error-invitation-code-not-exist": "Inbjudningskod finns inte",
     "error-invitation-code-not-exist": "Inbjudningskod finns inte",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/ta.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/th.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ ส่งคำเชิญให้คุณ",
     "email-invite-register-subject": "__inviter__ ส่งคำเชิญให้คุณ",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/tr.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ size bir davetiye gönderdi",
     "email-invite-register-subject": "__inviter__ size bir davetiye gönderdi",
     "email-invite-register-text": "Sevgili __user__,\n\n__inviter__ Sizi koordine çalışabilmek için Wekan'a davet etti.\n\nLütfen aşağıdaki linke tıklayın:\n__url__\n\nDavetiye kodunuz: __icode__\n\nTeşekkürler.",
     "email-invite-register-text": "Sevgili __user__,\n\n__inviter__ Sizi koordine çalışabilmek için Wekan'a davet etti.\n\nLütfen aşağıdaki linke tıklayın:\n__url__\n\nDavetiye kodunuz: __icode__\n\nTeşekkürler.",
     "error-invitation-code-not-exist": "Davetiye kodu bulunamadı",
     "error-invitation-code-not-exist": "Davetiye kodu bulunamadı",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/uk.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/vi.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-subject": "__inviter__ sent you an invitation",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "email-invite-register-text": "Dear __user__,\n\n__inviter__ invites you to Wekan for collaborations.\n\nPlease follow the link below:\n__url__\n\nAnd your invitation code is: __icode__\n\nThanks.",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
     "error-invitation-code-not-exist": "Invitation code doesn't exist",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/zh-CN.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ 向您发出邀请",
     "email-invite-register-subject": "__inviter__ 向您发出邀请",
     "email-invite-register-text": "亲爱的 __user__,\n\n__inviter__ 邀请您加入 Wekan 进行协作。\n\n请访问下面的链接︰\n__url__\n\n您的的邀请码是︰\n__icode__\n\n非常感谢。",
     "email-invite-register-text": "亲爱的 __user__,\n\n__inviter__ 邀请您加入 Wekan 进行协作。\n\n请访问下面的链接︰\n__url__\n\n您的的邀请码是︰\n__icode__\n\n非常感谢。",
     "error-invitation-code-not-exist": "邀请码不存在",
     "error-invitation-code-not-exist": "邀请码不存在",
-    "error-notAuthorized": "You are not authorized to view this page."
+    "error-notAuthorized": "You are not authorized to view this page.",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 3 - 1
i18n/zh-TW.i18n.json

@@ -360,5 +360,7 @@
     "email-invite-register-subject": "__inviter__ 向您發出邀請",
     "email-invite-register-subject": "__inviter__ 向您發出邀請",
     "email-invite-register-text": "親愛的 __user__,\n\n__inviter__ 邀請您加入 Wekan 一同協作\n\n請點擊下列連結:\n__url__\n\n您的邀請碼為:__icode__\n\n謝謝。",
     "email-invite-register-text": "親愛的 __user__,\n\n__inviter__ 邀請您加入 Wekan 一同協作\n\n請點擊下列連結:\n__url__\n\n您的邀請碼為:__icode__\n\n謝謝。",
     "error-invitation-code-not-exist": "邀請碼不存在",
     "error-invitation-code-not-exist": "邀請碼不存在",
-    "error-notAuthorized": "沒有適合的權限觀看"
+    "error-notAuthorized": "沒有適合的權限觀看",
+    "outgoing-webhooks": "Outgoing Webhooks",
+    "outgoingWebhooksPopup-title": "Outgoing Webhooks"
 }
 }

+ 5 - 0
models/activities.js

@@ -131,5 +131,10 @@ if (Meteor.isServer) {
     Notifications.getUsers(participants, watchers).forEach((user) => {
     Notifications.getUsers(participants, watchers).forEach((user) => {
       Notifications.notify(user, title, description, params);
       Notifications.notify(user, title, description, params);
     });
     });
+
+    const integration = Integrations.findOne({ boardId: board._id, type: 'outgoing-webhooks', enabled: true });
+    if (integration) {
+      Meteor.call('outgoingWebhooks', integration, description, params);
+    }
   });
   });
 }
 }

+ 54 - 0
models/integrations.js

@@ -0,0 +1,54 @@
+Integrations = new Mongo.Collection('integrations');
+
+Integrations.attachSchema(new SimpleSchema({
+  enabled: {
+    type: Boolean,
+    defaultValue: true,
+  },
+  title: {
+    type: String,
+    optional: true,
+  },
+  type: {
+    type: String,
+  },
+  url: { // URL validation regex (https://mathiasbynens.be/demo/url-regex)
+    type: String,
+  },
+  token: {
+    type: String,
+    optional: true,
+  },
+  boardId: {
+    type: String,
+  },
+  createdAt: {
+    type: Date,
+    denyUpdate: false,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert) {
+        return new Date();
+      } else {
+        this.unset();
+      }
+    },
+  },
+  userId: {
+    type: String,
+    autoValue() { // eslint-disable-line consistent-return
+      if (this.isInsert || this.isUpdate) {
+        return this.userId;
+      }
+    },
+  },
+}));
+
+Integrations.allow({
+  insert(userId, doc) {
+    return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+  },
+  update(userId, doc) {
+    return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
+  },
+  fetch: ['boardId'],
+});

+ 47 - 0
server/notifications/outgoing.js

@@ -0,0 +1,47 @@
+const postCatchError = Meteor.wrapAsync((url, options, resolve) => {
+  HTTP.post(url, options, (err, res) => {
+    if (err) {
+      resolve(null, err.response);
+    } else {
+      resolve(null, res);
+    }
+  });
+});
+
+Meteor.methods({
+  outgoingWebhooks(integration, description, params) {
+    check(integration, Object);
+    check(description, String);
+    check(params, Object);
+
+    const quoteParams = _.clone(params);
+    ['card', 'list', 'oldList', 'board', 'comment'].forEach((key) => {
+      if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`;
+    });
+
+    const user = Users.findOne(integration.userId);
+    const text = `${params.user} ${TAPi18n.__(description, quoteParams, user.getLanguage())}\n${params.url}`;
+
+    if (text.length === 0) return;
+
+    const value = {
+      text: `${text}`,
+    };
+
+    const options = {
+      headers: {
+        // 'Content-Type': 'application/json',
+        // 'X-Wekan-Activities-Token': 'Random.Id()',
+      },
+      data: value,
+    };
+
+    const response = postCatchError(integration.url, options);
+
+    if (response && response.statusCode && response.statusCode === 200) {
+      return true; // eslint-disable-line consistent-return
+    } else {
+      throw new Meteor.Error('error-invalid-webhook-response');
+    }
+  },
+});

+ 1 - 0
server/publications/boards.js

@@ -73,6 +73,7 @@ Meteor.publishRelations('board', function(boardId) {
     ],
     ],
   }, { limit: 1 }), function(boardId, board) {
   }, { limit: 1 }), function(boardId, board) {
     this.cursor(Lists.find({ boardId }));
     this.cursor(Lists.find({ boardId }));
+    this.cursor(Integrations.find({ boardId }));
 
 
     // Cards and cards comments
     // Cards and cards comments
     // XXX Originally we were publishing the card documents as a child of the
     // XXX Originally we were publishing the card documents as a child of the