redmine-mattermost 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b17e4d44f9c953dabfdd955d7970ab1630ea37f2
4
+ data.tar.gz: 861ca5d7a696c50dc9c01a1690ae7599ceee0e8e
5
+ SHA512:
6
+ metadata.gz: 2e95d3b82ecc49c62cdff20a3ca87a13d61464860e2fe98fd854cb027f8e1743628a2523cdfd73962bb92f5a62ac6552abca76ede3d76cbba3bcfae2af262837
7
+ data.tar.gz: 0a389640ad699753e24731d29768f24dc2df5dac2825c3a7db9c4fa1222ffb1b00336316a3ab70214bac93271dd0ef14d840243cf6d1537bae374bb3d1bfa980
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in redmine-mattermost.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,10 @@
1
+ Released under the MIT license.
2
+
3
+ Project redmine-slack is Copyright (c) 2014-2016 by Samuel Cormier-Iijima (https://github.com/sciyoshi/redmine-slack).
4
+ Fork project redmine_mattermost is Copyright (c) 2015-2016 by AltSol (https://github.com/altsol/redmine_mattermost).
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,60 @@
1
+ ### NOTE: This is a fork of [altsol/redmine_mattermost](https://github.com/altsol/redmine_mattermost) to support a Gem-based plugin for Redmine!
2
+
3
+ # Mattermost chat plugin for Redmine
4
+
5
+ This plugin posts updates to issues in your Redmine installation to a Mattermost
6
+ channel.
7
+
8
+ Redmine Supported versions: 2.0.x - 3.2.x.
9
+
10
+ ## Screenshot
11
+
12
+ ![screenshot](https://raw.githubusercontent.com/altsol/redmine_mattermost/assets/screenshot.png)
13
+
14
+ ## Installation
15
+
16
+ 1. Ensure you have a `Gemfile.local` file in your Redmine directory. You may just create an empty file.
17
+ 2. Register `redmine-mattermost` as a dependency as you would do for any other Ruby project:
18
+
19
+ gem "redmine-mattermost", "~> 0.3"
20
+
21
+ 3. Run bundler:
22
+
23
+ bundle install
24
+
25
+ Restart Redmine, and you should see the plugin show up in the Plugins page.
26
+ Under the configuration options, set the Mattermost API URL to the URL for an
27
+ Incoming WebHook integration in your Mattermost account (see also the next two sections).
28
+
29
+ ## Customized Routing
30
+
31
+ You can also route messages to different channels on a per-project basis. To
32
+ do this, create a project custom field (Administration > Custom fields > Project)
33
+ named `Mattermost Channel`. If no custom channel is defined for a project, the parent
34
+ project will be checked (or the default will be used). To prevent all notifications
35
+ from being sent for a project, set the custom channel to `-`.
36
+
37
+ For more information, see http://www.redmine.org/projects/redmine/wiki/Plugins (see also next section for an easy configuration demonstration).
38
+
39
+ ## Screenshot Guided Configuration
40
+
41
+ Step 1: Create an Incoming Webhook in Mattermost (Account Settings > Integrations > Incoming Webhooks).
42
+
43
+ ![step1](https://raw.githubusercontent.com/altsol/redmine_mattermost/assets/step1.png)
44
+
45
+ Step 2: Configure this Redmine plugin for Mattermost. For per-project customized routing, leave the `Mattermost Channel` field empty and follow the next steps, otherwise all Redmine projects will post to the same Mattermost channel. Be careful when filling the channel field, you need to input the channel's handle, not the display name visible to users. You can find each channel's handle by going inside the channel and click the down-arrow and selecting view info.
46
+
47
+ ![step3](https://raw.githubusercontent.com/altsol/redmine_mattermost/assets/step3.png)
48
+
49
+ Step 3: For per-project customized routing, first create the project custom field (Administration > Custom fields > Project).
50
+
51
+ ![step4a](https://raw.githubusercontent.com/altsol/redmine_mattermost/assets/step4a.png)
52
+ ![step4b](https://raw.githubusercontent.com/altsol/redmine_mattermost/assets/step4b.png)
53
+
54
+ Step 4: For per-project customized routing, configure the Mattermost channel handle inside your Redmine project.
55
+
56
+ ![step5](https://raw.githubusercontent.com/altsol/redmine_mattermost/assets/step5.png)
57
+
58
+ ## Credits
59
+
60
+ The source code is forked from https://github.com/altsol/redmine_mattermost. Special thanks to the original author and contributors for making this awesome hook for Redmine. This fork is just refactored to allow a Gem-based installation.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/gem_tasks"
@@ -0,0 +1,46 @@
1
+ <p>
2
+ <label for="settings_mattermost_url">Mattermost URL</label>
3
+ <input type="text" id="settings_mattermost_url" size=80 value="<%= settings['mattermost_url'] %>" name="settings[mattermost_url]" />
4
+ </p>
5
+
6
+ <p>
7
+ Generate an "Incoming WebHook" URL from the <a href="http://docs.mattermost.com/developer/webhooks-incoming.html">Integrations configuration page on Mattermost</a>. This URL can be changed on a per-project basis by creating a <a href="/custom_fields/new?type=ProjectCustomField">project custom field</a> named "Mattermost URL" (without quotes).
8
+ </p>
9
+
10
+ <p>
11
+ <label for="settings_channel">Mattermost Channel</label>
12
+ <input type="text" id="settings_channel" value="<%= settings['channel'] %>" name="settings[channel]" />
13
+ </p>
14
+
15
+ <p>
16
+ The channel can be changed on a per-project basis by creating a
17
+ <a href="/custom_fields/new?type=ProjectCustomField">project custom field</a> named "Mattermost Channel" (without quotes).
18
+ </p>
19
+
20
+ <p>
21
+ <label for="settings_icon">Mattermost Icon</label>
22
+ <input type="text" id="settings_icon" value="<%= settings['icon'] %>" name="settings[icon]" />
23
+ </p>
24
+
25
+ <p>
26
+ <label for="settings_username">Mattermost Username</label>
27
+ <input type="text" id="settings_username" value="<%= settings['username'] %>" name="settings[username]" />
28
+ </p>
29
+
30
+ <p>
31
+ <label for="settings_display_watchers">Display Watchers?</label>
32
+ <select id="settings_display_watchers" value="<%= settings['display_watchers'] %>" name="settings[display_watchers]">
33
+ <option value="yes">Yes</option>
34
+ <option value="no" <%= settings['display_watchers'] != 'yes' ? %q(selected="selected") : '' %>>No</option>
35
+ </select>
36
+ </p>
37
+
38
+ <p>
39
+ <label for="settings_post_updates">Post Issue Updates?</label>
40
+ <input type="checkbox" id="settings_post_updates" value="1" name="settings[post_updates]" <%= settings['post_updates'] == '1' ? 'checked="checked"' : '' %> />
41
+ </p>
42
+
43
+ <p>
44
+ <label for="settings_post_wiki_updates">Post Wiki Updates?</label>
45
+ <input type="checkbox" id="settings_post_wiki_updates" value="1" name="settings[post_wiki_updates]" <%= settings['post_wiki_updates'] == '1' ? 'checked="checked"' : '' %> />
46
+ </p>
@@ -0,0 +1,7 @@
1
+ require "redmine_mattermost/version"
2
+ require "redmine_mattermost/infos"
3
+ require "redmine_mattermost/engine"
4
+
5
+ # Mattermost chat integration
6
+ module RedmineMattermost
7
+ end
@@ -0,0 +1,10 @@
1
+ require "redmine_mattermost/redmine_plugin"
2
+
3
+ module RedmineMattermost
4
+ # Simple engine to support the Redmine plugin
5
+ class Engine < ::Rails::Engine
6
+ config.to_prepare do
7
+ RedminePlugin.new
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module RedmineMattermost
2
+ module Infos
3
+ NAME = "Redmine Mattermost"
4
+ DESCRIPTION = "Mattermost chat integration"
5
+ LICENSE = "MIT"
6
+ URL = "https://github.com/neopoly/redmine-mattermost"
7
+ AUTHORS = ["AltSol"]
8
+ AUTHOR_URL = "http://altsol.gr"
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ module RedmineMattermost
2
+ module IssuePatch
3
+ def self.included(base) # :nodoc:
4
+ base.extend(ClassMethods)
5
+ base.send(:include, InstanceMethods)
6
+
7
+ base.class_eval do
8
+ unloadable # Send unloadable so it will not be unloaded in development
9
+ after_create :create_from_issue
10
+ after_save :save_from_issue
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+ end
16
+
17
+ module InstanceMethods
18
+ def create_from_issue
19
+ @create_already_fired = true
20
+ Redmine::Hook.call_hook(:redmine_mattermost_issues_new_after_save, { :issue => self})
21
+ return true
22
+ end
23
+
24
+ def save_from_issue
25
+ if not @create_already_fired
26
+ Redmine::Hook.call_hook(:redmine_mattermost_issues_edit_after_save, { :issue => self, :journal => self.current_journal}) unless self.current_journal.nil?
27
+ end
28
+ return true
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,269 @@
1
+ require 'httpclient'
2
+
3
+ class MattermostListener < Redmine::Hook::Listener
4
+ def redmine_mattermost_issues_new_after_save(context={})
5
+ issue = context[:issue]
6
+
7
+ channel = channel_for_project issue.project
8
+ url = url_for_project issue.project
9
+
10
+ return unless channel and url
11
+ return if issue.is_private?
12
+
13
+ msg = "[#{escape issue.project}] #{escape issue.author} created <#{object_url issue}|#{escape issue}>#{mentions issue.description}"
14
+
15
+ attachment = {}
16
+ attachment[:text] = escape issue.description if issue.description
17
+ attachment[:fields] = [{
18
+ :title => I18n.t("field_status"),
19
+ :value => escape(issue.status.to_s),
20
+ :short => true
21
+ }, {
22
+ :title => I18n.t("field_priority"),
23
+ :value => escape(issue.priority.to_s),
24
+ :short => true
25
+ }, {
26
+ :title => I18n.t("field_assigned_to"),
27
+ :value => escape(issue.assigned_to.to_s),
28
+ :short => true
29
+ }]
30
+
31
+ attachment[:fields] << {
32
+ :title => I18n.t("field_watcher"),
33
+ :value => escape(issue.watcher_users.join(', ')),
34
+ :short => true
35
+ } if Setting.plugin_redmine_mattermost[:display_watchers] == 'yes'
36
+
37
+ speak msg, channel, attachment, url
38
+ end
39
+
40
+ def redmine_mattermost_issues_edit_after_save(context={})
41
+ issue = context[:issue]
42
+ journal = context[:journal]
43
+
44
+ channel = channel_for_project issue.project
45
+ url = url_for_project issue.project
46
+
47
+ return unless channel and url and Setting.plugin_redmine_mattermost[:post_updates] == '1'
48
+ return if issue.is_private?
49
+
50
+ msg = "[#{escape issue.project}] #{escape journal.user.to_s} updated <#{object_url issue}|#{escape issue}>#{mentions journal.notes}"
51
+
52
+ attachment = {}
53
+ attachment[:text] = escape journal.notes if journal.notes
54
+ attachment[:fields] = journal.details.map { |d| detail_to_field d }
55
+
56
+ speak msg, channel, attachment, url
57
+ end
58
+
59
+ def model_changeset_scan_commit_for_issue_ids_pre_issue_update(context={})
60
+ issue = context[:issue]
61
+ journal = issue.current_journal
62
+ changeset = context[:changeset]
63
+
64
+ channel = channel_for_project issue.project
65
+ url = url_for_project issue.project
66
+
67
+ return unless channel and url and issue.save
68
+ return if issue.is_private?
69
+
70
+ msg = "[#{escape issue.project}] #{escape journal.user.to_s} updated <#{object_url issue}|#{escape issue}>"
71
+
72
+ repository = changeset.repository
73
+
74
+ if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
75
+ host, port, prefix = $2, $4, $5
76
+ revision_url = Rails.application.routes.url_for(
77
+ :controller => 'repositories',
78
+ :action => 'revision',
79
+ :id => repository.project,
80
+ :repository_id => repository.identifier_param,
81
+ :rev => changeset.revision,
82
+ :host => host,
83
+ :protocol => Setting.protocol,
84
+ :port => port,
85
+ :script_name => prefix
86
+ )
87
+ else
88
+ revision_url = Rails.application.routes.url_for(
89
+ :controller => 'repositories',
90
+ :action => 'revision',
91
+ :id => repository.project,
92
+ :repository_id => repository.identifier_param,
93
+ :rev => changeset.revision,
94
+ :host => Setting.host_name,
95
+ :protocol => Setting.protocol
96
+ )
97
+ end
98
+
99
+ attachment = {}
100
+ attachment[:text] = ll(Setting.default_language, :text_status_changed_by_changeset, "<#{revision_url}|#{escape changeset.comments}>")
101
+ attachment[:fields] = journal.details.map { |d| detail_to_field d }
102
+
103
+ speak msg, channel, attachment, url
104
+ end
105
+
106
+ def controller_wiki_edit_after_save(context = { })
107
+ return unless Setting.plugin_redmine_mattermost[:post_wiki_updates] == '1'
108
+
109
+ project = context[:project]
110
+ page = context[:page]
111
+
112
+ user = page.content.author
113
+ project_url = "<#{object_url project}|#{escape project}>"
114
+ page_url = "<#{object_url page}|#{page.title}>"
115
+ comment = "[#{project_url}] #{page_url} updated by *#{user}*"
116
+
117
+ channel = channel_for_project project
118
+ url = url_for_project project
119
+
120
+ attachment = nil
121
+ if not page.content.comments.empty?
122
+ attachment = {}
123
+ attachment[:text] = "#{escape page.content.comments}"
124
+ end
125
+
126
+ speak comment, channel, attachment, url
127
+ end
128
+
129
+ def speak(msg, channel, attachment=nil, url=nil)
130
+ url = Setting.plugin_redmine_mattermost[:mattermost_url] if not url
131
+ username = Setting.plugin_redmine_mattermost[:username]
132
+ icon = Setting.plugin_redmine_mattermost[:icon]
133
+
134
+ params = {
135
+ :text => msg,
136
+ :link_names => 1,
137
+ }
138
+
139
+ params[:username] = username if username
140
+ params[:channel] = channel if channel
141
+
142
+ params[:attachments] = [attachment] if attachment
143
+
144
+ if icon and not icon.empty?
145
+ if icon.start_with? ':'
146
+ params[:icon_emoji] = icon
147
+ else
148
+ params[:icon_url] = icon
149
+ end
150
+ end
151
+
152
+ begin
153
+ client = HTTPClient.new
154
+ client.ssl_config.cert_store.set_default_paths
155
+ client.ssl_config.ssl_version = "SSLv23"
156
+ client.post_async url, {:payload => params.to_json}
157
+ rescue
158
+ # Bury exception if connection error
159
+ end
160
+ end
161
+
162
+ private
163
+ def escape(msg)
164
+ msg.to_s.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
165
+ end
166
+
167
+ def object_url(obj)
168
+ if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
169
+ host, port, prefix = $2, $4, $5
170
+ Rails.application.routes.url_for(obj.event_url({:host => host, :protocol => Setting.protocol, :port => port, :script_name => prefix}))
171
+ else
172
+ Rails.application.routes.url_for(obj.event_url({:host => Setting.host_name, :protocol => Setting.protocol}))
173
+ end
174
+ end
175
+
176
+ def url_for_project(proj)
177
+ return nil if proj.blank?
178
+
179
+ cf = ProjectCustomField.find_by_name("Mattermost URL")
180
+
181
+ return [
182
+ (proj.custom_value_for(cf).value rescue nil),
183
+ (url_for_project proj.parent),
184
+ Setting.plugin_redmine_mattermost[:mattermost_url],
185
+ ].find{|v| v.present?}
186
+ end
187
+
188
+ def channel_for_project(proj)
189
+ return nil if proj.blank?
190
+
191
+ cf = ProjectCustomField.find_by_name("Mattermost Channel")
192
+
193
+ val = [
194
+ (proj.custom_value_for(cf).value rescue nil),
195
+ (channel_for_project proj.parent),
196
+ Setting.plugin_redmine_mattermost[:channel],
197
+ ].find{|v| v.present?}
198
+
199
+ # Channel name '-' is reserved for NOT notifying
200
+ return nil if val.to_s == '-'
201
+ val
202
+ end
203
+
204
+ def detail_to_field(detail)
205
+ if detail.property == "cf"
206
+ key = CustomField.find(detail.prop_key).name rescue nil
207
+ title = key
208
+ elsif detail.property == "attachment"
209
+ key = "attachment"
210
+ title = I18n.t :label_attachment
211
+ else
212
+ key = detail.prop_key.to_s.sub("_id", "")
213
+ title = I18n.t "field_#{key}"
214
+ end
215
+
216
+ short = true
217
+ value = escape detail.value.to_s
218
+
219
+ case key
220
+ when "title", "subject", "description"
221
+ short = false
222
+ when "tracker"
223
+ tracker = Tracker.find(detail.value) rescue nil
224
+ value = escape tracker.to_s
225
+ when "project"
226
+ project = Project.find(detail.value) rescue nil
227
+ value = escape project.to_s
228
+ when "status"
229
+ status = IssueStatus.find(detail.value) rescue nil
230
+ value = escape status.to_s
231
+ when "priority"
232
+ priority = IssuePriority.find(detail.value) rescue nil
233
+ value = escape priority.to_s
234
+ when "category"
235
+ category = IssueCategory.find(detail.value) rescue nil
236
+ value = escape category.to_s
237
+ when "assigned_to"
238
+ user = User.find(detail.value) rescue nil
239
+ value = escape user.to_s
240
+ when "fixed_version"
241
+ version = Version.find(detail.value) rescue nil
242
+ value = escape version.to_s
243
+ when "attachment"
244
+ attachment = Attachment.find(detail.prop_key) rescue nil
245
+ value = "<#{object_url attachment}|#{escape attachment.filename}>" if attachment
246
+ when "parent"
247
+ issue = Issue.find(detail.value) rescue nil
248
+ value = "<#{object_url issue}|#{escape issue}>" if issue
249
+ end
250
+
251
+ value = "-" if value.empty?
252
+
253
+ result = { :title => title, :value => value }
254
+ result[:short] = true if short
255
+ result
256
+ end
257
+
258
+ def mentions text
259
+ return nil if text.nil?
260
+ names = extract_usernames text
261
+ names.present? ? "\nTo: " + names.join(', ') : nil
262
+ end
263
+
264
+ def extract_usernames text = ''
265
+ # mattermost usernames may only contain lowercase letters, numbers,
266
+ # dashes and underscores and must start with a letter or number.
267
+ text.scan(/@[a-z0-9][a-z0-9_\-]*/).uniq
268
+ end
269
+ end
@@ -0,0 +1,48 @@
1
+ module RedmineMattermost
2
+ # Registers this gem a Redmine plugin and applies the needed patches
3
+ class RedminePlugin
4
+ include RedmineMattermost::Infos
5
+
6
+ DEFAULT_SETTINGS = {
7
+ "callback_url" => "http://example.com/callback/",
8
+ "channel" => nil,
9
+ "icon" => "https://raw.githubusercontent.com/altsol/redmine_mattermost/assets/icon.png",
10
+ "username" => "redmine",
11
+ "display_watchers" => "no"
12
+ }.freeze
13
+
14
+ SETTING_PARTIAL = "settings/mattermost_settings"
15
+
16
+ def initialize
17
+ register!
18
+ boot!
19
+ end
20
+
21
+ private
22
+
23
+ def register!
24
+ Redmine::Plugin.register :redmine_mattermost do
25
+ name NAME
26
+ author AUTHORS.join(", ")
27
+ description DESCRIPTION
28
+ version VERSION
29
+ url URL
30
+ author_url AUTHOR_URL
31
+ directory Engine.root
32
+
33
+ requires_redmine version_or_higher: "2.0.0"
34
+
35
+ settings default: DEFAULT_SETTINGS, partial: SETTING_PARTIAL
36
+ end
37
+ end
38
+
39
+ def boot!
40
+ require "redmine_mattermost/listener"
41
+ require "redmine_mattermost/issue_patch"
42
+
43
+ unless Issue.included_modules.include? RedmineMattermost::IssuePatch
44
+ Issue.send(:include, RedmineMattermost::IssuePatch)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module RedmineMattermost
2
+ VERSION = "0.3"
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "redmine_mattermost/version"
5
+ require "redmine_mattermost/infos"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "redmine-mattermost"
9
+ spec.version = RedmineMattermost::VERSION
10
+ spec.authors = RedmineMattermost::Infos::AUTHORS
11
+ spec.summary = RedmineMattermost::Infos::DESCRIPTION
12
+ spec.description = RedmineMattermost::Infos::DESCRIPTION
13
+ spec.homepage = RedmineMattermost::Infos::URL
14
+ spec.license = RedmineMattermost::Infos::LICENSE
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rails", "~> 4.2.0"
22
+ spec.add_dependency "httpclient", "~> 2.8.2"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redmine-mattermost
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.3'
5
+ platform: ruby
6
+ authors:
7
+ - AltSol
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-09-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: httpclient
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.8.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.8.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ description: Mattermost chat integration
70
+ email:
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - ".gitignore"
76
+ - Gemfile
77
+ - LICENSE
78
+ - README.md
79
+ - Rakefile
80
+ - app/views/settings/_mattermost_settings.html.erb
81
+ - lib/redmine-mattermost.rb
82
+ - lib/redmine_mattermost/engine.rb
83
+ - lib/redmine_mattermost/infos.rb
84
+ - lib/redmine_mattermost/issue_patch.rb
85
+ - lib/redmine_mattermost/listener.rb
86
+ - lib/redmine_mattermost/redmine_plugin.rb
87
+ - lib/redmine_mattermost/version.rb
88
+ - redmine-mattermost.gemspec
89
+ homepage: https://github.com/neopoly/redmine-mattermost
90
+ licenses:
91
+ - MIT
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.4.6
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Mattermost chat integration
113
+ test_files: []