From 54661d3d44a7d98ed52680b074b2caed7aa33676 Mon Sep 17 00:00:00 2001
From: Sebastian Klier <sebastian@sebastianklier.com>
Date: Sun, 28 Feb 2016 15:26:52 +0800
Subject: [PATCH] add slack notifications for wiki pages

update changelog
---
 CHANGELOG                                     |  1 +
 .../projects/services_controller.rb           |  2 +-
 app/controllers/projects/wikis_controller.rb  |  6 +-
 app/models/hooks/project_hook.rb              |  1 +
 app/models/project_services/slack_service.rb  |  5 +-
 .../slack_service/wiki_page_message.rb        | 53 ++++++++++++++++++
 app/models/service.rb                         |  4 +-
 app/models/wiki_page.rb                       |  4 ++
 app/services/wiki_pages/base_service.rb       | 26 +++++++++
 app/services/wiki_pages/create_service.rb     | 13 +++++
 app/services/wiki_pages/update_service.rb     | 11 ++++
 app/views/shared/_service_settings.html.haml  |  8 +++
 ...0227120001_add_event_field_for_web_hook.rb |  5 ++
 .../20160227120047_add_event_to_services.rb   |  5 ++
 db/schema.rb                                  |  2 +
 .../slack_service/wiki_page_message_spec.rb   | 56 +++++++++++++++++++
 .../project_services/slack_service_spec.rb    | 17 ++++++
 17 files changed, 213 insertions(+), 6 deletions(-)
 create mode 100644 app/models/project_services/slack_service/wiki_page_message.rb
 create mode 100644 app/services/wiki_pages/base_service.rb
 create mode 100644 app/services/wiki_pages/create_service.rb
 create mode 100644 app/services/wiki_pages/update_service.rb
 create mode 100644 db/migrate/20160227120001_add_event_field_for_web_hook.rb
 create mode 100644 db/migrate/20160227120047_add_event_to_services.rb
 create mode 100644 spec/models/project_services/slack_service/wiki_page_message_spec.rb

diff --git a/CHANGELOG b/CHANGELOG
index 01635cff02a..5082fe0fad4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -274,6 +274,7 @@ v 8.5.3
   - Show commit message in JIRA mention comment
   - Makes issue page and merge request page usable on mobile browsers.
   - Improved UI for profile settings
+  - Add Slack notifications when Wiki is edited (Sebastian Klier)
 
 v 8.5.2
   - Fix sidebar overlapping content when screen width was below 1200px
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 8b2577aebe1..739681f4085 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -6,7 +6,7 @@ class Projects::ServicesController < Projects::ApplicationController
                     :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
                     :colorize_messages, :channels,
                     :push_events, :issues_events, :merge_requests_events, :tag_push_events,
-                    :note_events, :build_events,
+                    :note_events, :build_events, :wiki_page_events,
                     :notify_only_broken_builds, :add_pusher,
                     :send_from_committer_email, :disable_diffs, :external_wiki_url,
                     :notify, :color,
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 9f3a4a69721..6ffcba8d906 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -44,7 +44,7 @@ class Projects::WikisController < Projects::ApplicationController
 
     return render('empty') unless can?(current_user, :create_wiki, @project)
 
-    if @page.update(content, format, message)
+    if @page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page)
       redirect_to(
         namespace_project_wiki_path(@project.namespace, @project, @page),
         notice: 'Wiki was successfully updated.'
@@ -55,9 +55,9 @@ class Projects::WikisController < Projects::ApplicationController
   end
 
   def create
-    @page = WikiPage.new(@project_wiki)
+    @page = WikiPages::CreateService.new(@project, current_user, wiki_params).execute
 
-    if @page.create(wiki_params)
+    if @page
       redirect_to(
         namespace_project_wiki_path(@project.namespace, @project, @page),
         notice: 'Wiki was successfully updated.'
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index 7365e360de2..bc6e0f98c3c 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -25,4 +25,5 @@ class ProjectHook < WebHook
   scope :note_hooks, -> { where(note_events: true) }
   scope :merge_request_hooks, -> { where(merge_requests_events: true) }
   scope :build_hooks, -> { where(build_events: true) }
+  scope :wiki_page_hooks, ->  { where(wiki_page_events: true) }
 end
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index d89cf6d17b2..fd65027f084 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -60,7 +60,7 @@ class SlackService < Service
   end
 
   def supported_events
-    %w(push issue merge_request note tag_push build)
+    %w(push issue merge_request note tag_push build wiki_page)
   end
 
   def execute(data)
@@ -90,6 +90,8 @@ class SlackService < Service
         NoteMessage.new(data)
       when "build"
         BuildMessage.new(data) if should_build_be_notified?(data)
+      when "wiki_page"
+        WikiPageMessage.new(data)
       end
 
     opt = {}
@@ -133,3 +135,4 @@ require "slack_service/push_message"
 require "slack_service/merge_message"
 require "slack_service/note_message"
 require "slack_service/build_message"
+require "slack_service/wiki_page_message"
diff --git a/app/models/project_services/slack_service/wiki_page_message.rb b/app/models/project_services/slack_service/wiki_page_message.rb
new file mode 100644
index 00000000000..61134054859
--- /dev/null
+++ b/app/models/project_services/slack_service/wiki_page_message.rb
@@ -0,0 +1,53 @@
+class SlackService
+  class WikiPageMessage < BaseMessage
+    attr_reader :user_name
+    attr_reader :title
+    attr_reader :project_name
+    attr_reader :project_url
+    attr_reader :wiki_page_url
+    attr_reader :action
+    attr_reader :description
+
+    def initialize(params)
+      @user_name = params[:user][:name]
+      @project_name = params[:project_name]
+      @project_url = params[:project_url]
+
+      obj_attr = params[:object_attributes]
+      obj_attr = HashWithIndifferentAccess.new(obj_attr)
+      @title = obj_attr[:title]
+      @wiki_page_url = obj_attr[:url]
+      @description = obj_attr[:content]
+
+      @action = \
+        case obj_attr[:action]
+        when "create"
+          "created"
+        when "update"
+          "edited"
+        end
+    end
+
+    def attachments
+      description_message
+    end
+
+    private
+
+    def message
+      "#{user_name} #{action} #{wiki_page_link} in #{project_link}: *#{title}*"
+    end
+
+    def description_message
+      [{ text: format(@description), color: attachment_color }]
+    end
+
+    def project_link
+      "[#{project_name}](#{project_url})"
+    end
+
+    def wiki_page_link
+      "[wiki page](#{wiki_page_url})"
+    end
+  end
+end
diff --git a/app/models/service.rb b/app/models/service.rb
index 721273250ea..2645b8321d7 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -32,6 +32,7 @@ class Service < ActiveRecord::Base
   default_value_for :tag_push_events, true
   default_value_for :note_events, true
   default_value_for :build_events, true
+  default_value_for :wiki_page_events, true
 
   after_initialize :initialize_properties
 
@@ -53,6 +54,7 @@ class Service < ActiveRecord::Base
   scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) }
   scope :note_hooks, -> { where(note_events: true, active: true) }
   scope :build_hooks, -> { where(build_events: true, active: true) }
+  scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
 
   default_value_for :category, 'common'
 
@@ -94,7 +96,7 @@ class Service < ActiveRecord::Base
   end
 
   def supported_events
-    %w(push tag_push issue merge_request)
+    %w(push tag_push issue merge_request wiki_page)
   end
 
   def execute(data)
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 526760779a4..3d5fd9d3ee9 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -29,6 +29,10 @@ class WikiPage
   # new Page values before writing to the Gollum repository.
   attr_accessor :attributes
 
+  def hook_attrs
+    attributes
+  end
+
   def initialize(wiki, page = nil, persisted = false)
     @wiki       = wiki
     @page       = page
diff --git a/app/services/wiki_pages/base_service.rb b/app/services/wiki_pages/base_service.rb
new file mode 100644
index 00000000000..e4c0506e20c
--- /dev/null
+++ b/app/services/wiki_pages/base_service.rb
@@ -0,0 +1,26 @@
+module WikiPages
+  class BaseService < ::BaseService
+
+    def hook_data(page, action)
+      hook_data = {
+        object_kind: page.class.name.underscore,
+        user: current_user.hook_attrs,
+        project: @project.hook_attrs,
+        object_attributes: page.hook_attrs,
+        # DEPRECATED
+        repository: @project.hook_attrs.slice(:name, :url, :description, :homepage)
+      }
+      page_url = "#{Gitlab.config.gitlab.url}#{@project.wiki.wiki_base_path}/#{page.slug}"
+      hook_data[:object_attributes].merge!(url: page_url, action: action)
+      hook_data
+    end
+
+    private
+
+    def execute_hooks(page, action = 'create')
+      page_data = hook_data(page, action)
+      @project.execute_hooks(page_data, :wiki_page_hooks)
+      @project.execute_services(page_data, :wiki_page_hooks)
+    end
+  end
+end
diff --git a/app/services/wiki_pages/create_service.rb b/app/services/wiki_pages/create_service.rb
new file mode 100644
index 00000000000..988c663b9d0
--- /dev/null
+++ b/app/services/wiki_pages/create_service.rb
@@ -0,0 +1,13 @@
+module WikiPages
+  class CreateService < WikiPages::BaseService
+    def execute
+      page = WikiPage.new(@project.wiki)
+
+      if page.create(@params)
+        execute_hooks(page, 'create')
+      end
+
+      page
+    end
+  end
+end
diff --git a/app/services/wiki_pages/update_service.rb b/app/services/wiki_pages/update_service.rb
new file mode 100644
index 00000000000..8f6a50da838
--- /dev/null
+++ b/app/services/wiki_pages/update_service.rb
@@ -0,0 +1,11 @@
+module WikiPages
+  class UpdateService < WikiPages::BaseService
+    def execute(page)
+      if page.update(@params[:content], @params[:format], @params[:message])
+        execute_hooks(page, 'update')
+      end
+
+      page
+    end
+  end
+end
diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml
index fc935166bf6..4eaf7c2a025 100644
--- a/app/views/shared/_service_settings.html.haml
+++ b/app/views/shared/_service_settings.html.haml
@@ -62,6 +62,14 @@
               %strong Build events
             %p.light
               This url will be triggered when a build status changes
+      - if @service.supported_events.include?("wiki_page")
+        %div
+          = form.check_box :wiki_page_events, class: 'pull-left'
+          .prepend-left-20
+            = form.label :wiki_page_events, class: 'list-label' do
+              %strong Wiki Page events
+            %p.light
+              This url will be triggered when a wiki page is created/updated
 
 
 - @service.fields.each do |field|
diff --git a/db/migrate/20160227120001_add_event_field_for_web_hook.rb b/db/migrate/20160227120001_add_event_field_for_web_hook.rb
new file mode 100644
index 00000000000..65f2a47bb3c
--- /dev/null
+++ b/db/migrate/20160227120001_add_event_field_for_web_hook.rb
@@ -0,0 +1,5 @@
+class AddEventFieldForWebHook < ActiveRecord::Migration
+  def change
+    add_column :web_hooks, :wiki_page_events, :boolean, default: false, null: false
+  end
+end
diff --git a/db/migrate/20160227120047_add_event_to_services.rb b/db/migrate/20160227120047_add_event_to_services.rb
new file mode 100644
index 00000000000..879f4b96b12
--- /dev/null
+++ b/db/migrate/20160227120047_add_event_to_services.rb
@@ -0,0 +1,5 @@
+class AddEventToServices < ActiveRecord::Migration
+  def change
+    add_column :services, :wiki_page_events, :boolean, :default => true
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 7cecbf16f66..d82c8c1e257 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -820,6 +820,7 @@ ActiveRecord::Schema.define(version: 20160419120017) do
     t.boolean  "build_events",          default: false,    null: false
     t.string   "category",              default: "common", null: false
     t.boolean  "default",               default: false
+    t.boolean  "wiki_page_events",      default: true
   end
 
   add_index "services", ["category"], name: "index_services_on_category", using: :btree
@@ -1014,6 +1015,7 @@ ActiveRecord::Schema.define(version: 20160419120017) do
     t.boolean  "note_events",                          default: false,         null: false
     t.boolean  "enable_ssl_verification",              default: true
     t.boolean  "build_events",                         default: false,         null: false
+    t.boolean  "wiki_page_events",                     default: false,         null: false
   end
 
   add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree
diff --git a/spec/models/project_services/slack_service/wiki_page_message_spec.rb b/spec/models/project_services/slack_service/wiki_page_message_spec.rb
new file mode 100644
index 00000000000..c272e0e0aac
--- /dev/null
+++ b/spec/models/project_services/slack_service/wiki_page_message_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe SlackService::WikiPageMessage, models: true do
+  subject { SlackService::WikiPageMessage.new(args) }
+
+  let(:args) do
+    {
+      user: {
+        name: 'Test User',
+        username: 'Test User'
+      },
+      project_name: 'project_name',
+      project_url: 'somewhere.com',
+
+      object_attributes: {
+        title: 'Wiki page title',
+        url: 'url',
+        action: 'create',
+        content: 'Wiki page description'
+      }
+    }
+  end
+
+  let(:color) { '#345' }
+
+  context 'create' do
+    it 'returns a message regarding creation of pages' do
+      expect(subject.pretext).to eq(
+        'Test User created <url|wiki page> in <somewhere.com|project_name>: '\
+        '*Wiki page title*')
+      expect(subject.attachments).to eq([
+        {
+          text: "Wiki page description",
+          color: color,
+        }
+      ])
+    end
+  end
+
+  context 'update' do
+    before do
+      args[:object_attributes][:action] = 'update'
+    end
+    it 'returns a message regarding updating of pages' do
+      expect(subject.pretext). to eq(
+        'Test User edited <url|wiki page> in <somewhere.com|project_name>: '\
+        '*Wiki page title*')
+      expect(subject.attachments).to eq([
+        {
+          text: "Wiki page description",
+          color: color,
+        }
+      ])
+    end
+  end
+end
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
index a9e0afad90f..478d59be08b 100644
--- a/spec/models/project_services/slack_service_spec.rb
+++ b/spec/models/project_services/slack_service_spec.rb
@@ -75,6 +75,17 @@ describe SlackService, models: true do
       @merge_request = merge_service.execute
       @merge_sample_data = merge_service.hook_data(@merge_request,
                                                    'open')
+
+      opts = {
+        title: "Awesome wiki_page",
+        content: "Some text describing some thing or another",
+        format: "md",
+        message: "user created page: Awesome wiki_page"
+      }
+
+      wiki_page_service = WikiPages::CreateService.new(project, user, opts)
+      @wiki_page = wiki_page_service.execute
+      @wiki_page_sample_data = wiki_page_service.hook_data(@wiki_page, 'create')
     end
 
     it "should call Slack API for push events" do
@@ -95,6 +106,12 @@ describe SlackService, models: true do
       expect(WebMock).to have_requested(:post, webhook_url).once
     end
 
+    it "should call Slack API for wiki page events" do
+      slack.execute(@wiki_page_sample_data)
+
+      expect(WebMock).to have_requested(:post, webhook_url).once
+    end
+
     it 'should use the username as an option for slack when configured' do
       allow(slack).to receive(:username).and_return(username)
       expect(Slack::Notifier).to receive(:new).
-- 
GitLab