Crowiでページ削除時にslack通知が飛ぶようにしてみた。

研究室Wikiとして[Crowi](http://site.crowi.wiki/)を導入しました。大変便利なのですが、少しだけ不満点があったのでパパっと機能を追加してみた話。

やった事としては、

* ページ名変更/ページ削除時のslack通知
* ページ新規作成、編集時のslack「強制」通知

の２つです。

# Crowiとは
※知っている人は読み飛ばしてください。

Crowiとは、Wiki的なwebアプリです。
Markdownで書けます。slackに通知も飛ばせます。ユーザー管理も簡単にできます。

この記事（[研究室に最強の wiki と噂されている Crowi を Docker/CoreOS で構築してみた](https://qiita.com/nishimuraatsushi/items/52a5cc9ecd10146b083f)）にもありますが、研究室Wiki用途としては最強なのではないでしょうか。

そうそう、インストールの際はこの記事（[オープンソース Wiki「Crowi」を CentOS 7 にインストールする](https://qiita.com/bezeklik/items/48fe054c2df05f03f3d1)）が大変参考になりました。

# きっかけ
Crowiの構築が完了して、slack連携まで設定した所で思った事。

「Crowiってページの作成や編集時に通知を飛ばしてくれるけど、削除された時には通知を飛ばしてくれないよね...？」

研究室という閉じた組織なので荒らしは無いとは思いますが、消したページに実は重要な情報が残ってた、とかあるじゃないですか。「こんなページ消して大丈夫だろ」って思って消したけど、他の人からすると「いつの間にか消されていた(´・ω・｀)」ってなる事も。。。

もちろんバックアップがあれば復元できるわけですが、消えた時に通知が飛べば、その場で「そのページ、僕にとっては大事だから残しておいて！」って言えますよね。

というわけでページ削除時にも通知が飛ぶようにしてみました。

あと、ページ編集時に「常に」通知が飛ぶようにもしました。ネトストが捗るじゃん？

# 環境
Crowi: v1.6.2
nodejs: v6.11.3

# 変更箇所
diffで説明します。
やっつけ仕事なので実装は結構雑です、、、

```
diff --git a/lib/routes/page.js b/lib/routes/page.js
index 1d7e1ff..89b450e 100644
--- a/lib/routes/page.js
+++ b/lib/routes/page.js
@@ -364,6 +364,7 @@ module.exports = function(crowi, app) {
       }

       if (data) {
+        updateOrCreate = 'update';
         previousRevision = data.revision;
         return Page.updatePage(data, body, req.user, {grant: grant});
       } else {
@@ -379,7 +380,7 @@ module.exports = function(crowi, app) {
       }
       // TODO: move to events
       if (notify.slack) {
-        if (notify.slack.on && notify.slack.channel) {
+       if (/*notify.slack.on*/ true && notify.slack.channel) {
           data.updateSlackChannel(notify.slack.channel).then(function(){}).catch(function(){});

           if (crowi.slack) {
@@ -787,7 +788,7 @@ module.exports = function(crowi, app) {
       debug('Delete page', pageData._id, pageData.path);

       if (isCompletely) {
-        return Page.completelyDeletePage(pageData, req.user);
+       return Page.completelyDeletePage(pageData, req.user);
       }

       // else
@@ -798,6 +799,19 @@ module.exports = function(crowi, app) {
       return Page.deletePage(pageData, req.user);
     }).then(function(data) {
       debug('Page deleted', data.path);
+
+      if (!data) {
+        throw new Error('Data not found');
+      }
+      data.updateSlackChannel("wiki").then(function(){}).catch(function(){});
+
+      if (crowi.slack) {
+         ["wiki"].map(function(chan) {
+                 var message = crowi.slack.prepareSlackMessage(data, req.user, chan, 'delete', false);
+                 crowi.slack.post(message).then(function(){}).catch(function(){});
+             });
+      }
+
       var result = {};
       result.page = data;

@@ -825,6 +839,20 @@ module.exports = function(crowi, app) {
       return Page.revertDeletedPage(pageData, req.user);
     }).then(function(data) {
       debug('Complete to revert deleted page', data.path);
+
+      if (!data) {
+        throw new Error('Data not found');
+      }
+      pageData = data;
+      data.updateSlackChannel("wiki").then(function(){}).catch(function(){});
+
+      if (crowi.slack) {
+         ["wiki"].map(function(chan) {
+                 var message = crowi.slack.prepareSlackMessage(pageData, req.user, chan, 'revert', false);
+                 crowi.slack.post(message).then(function(){}).catch(function(){});
+             });
+      }
+
       var result = {};
       result.page = data;

@@ -875,6 +903,15 @@ module.exports = function(crowi, app) {

         return Page.rename(pageData, newPagePath, req.user, options);
       }).then(function() {
+             page.updateSlackChannel("wiki").then(function(){}).catch(function(){});
+
+             if (crowi.slack) {
+                 ["wiki"].map(function(chan) {
+                         var message = crowi.slack.prepareSlackMessage(page, req.user, chan, 'rename', false);
+                         crowi.slack.post(message).then(function(){}).catch(function(){});
+                     });
+             }
+
         var result = {};
         result.page = page;

```
また、notify.slack.onを参照しない事で常にslackに通知が飛ぶようにしています。

本当は管理画面辺りにどのチャンネルに通知を飛ばすかを設定するインターフェースを実装するのが良いのでしょうが、どうせ自分の研究室で使うだけなので #wiki に飛ばすようハードコーディングしてしまいました。

```
diff --git a/lib/util/slack.js b/lib/util/slack.js
index d65095b..e7e96b4 100644
--- a/lib/util/slack.js
+++ b/lib/util/slack.js
@@ -148,10 +148,10 @@ module.exports = function(crowi) {
     var url = config.crowi['app:url'] || '';
     var body = page.revision.body;

-    if (updateType == 'create') {
-      body = this.prepareAttachmentTextForCreate(page, user);
-    } else {
+    if (updateType == 'update') {
       body = this.prepareAttachmentTextForUpdate(page, user, previousRevision);
+    } else {
+      body = this.prepareAttachmentTextForCreate(page, user);
     }

     var attachment = {
@@ -183,8 +183,16 @@ module.exports = function(crowi) {

     if (updateType == 'create') {
       text = sprintf(':white_check_mark: %s created a new page! %s', user.username, path);
-    } else {
+    } else if (updateType == 'update') {
       text = sprintf(':up: %s updated %s', user.username, path);
+    } else if (updateType == 'delete') {
+      text = sprintf(':wastebasket: %s deleted %s', user.username, path);
+    } else if (updateType == 'revert') {
+      text = sprintf(':recycle: %s reverted %s from trash', user.username, path);
+    } else if (updateType == 'rename') {
+      text = sprintf(':arrow_heading_up: %s renamed %s', user.username, path);
+    } else {
+      text = sprintf(':question: unknown action');
     }

     return text;
```
slackへの投稿メッセージを追加。

```
diff --git a/lib/views/_form.html b/lib/views/_form.html
index 0927e82..e8eb31d 100644
--- a/lib/views/_form.html
+++ b/lib/views/_form.html
@@ -22,7 +22,7 @@

       <div class="pull-right form-inline page-form-setting" id="page-form-setting" data-slack-configured="{{ slackConfigured() }}">
         {% if slackConfigured() %}
-        <span class="input-group extended-setting">
+        <span class="input-group extended-setting" style="display:none">
           <span class="input-group-addon">
             <label>
               <i class="fa fa-slack"></i>
```
slackに強制的に通知が飛ぶようになったので、ページ編集画面のslackへの通知可否のチェックボックスもいりません。本当はCSSで消すのが良いのでしょうが、面倒だったのでHTMLに直書きしています。

# 動いた

<img width="492" alt="スクリーンショット 2017-09-29 22.54.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/84305/8822c853-c84b-b992-2e6e-9a84a1c2d4a1.png">

ゴミ箱に移動した時とゴミ箱から削除（完全削除）した時とでそれぞれ削除メッセージが出ます。

これでページを削除されてもすぐ気づけちゃうぞ！
わぁい、ネトストサイコーーー！！！