redmine-mattermost 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/LICENSE +10 -0
- data/README.md +60 -0
- data/Rakefile +3 -0
- data/app/views/settings/_mattermost_settings.html.erb +46 -0
- data/lib/redmine-mattermost.rb +7 -0
- data/lib/redmine_mattermost/engine.rb +10 -0
- data/lib/redmine_mattermost/infos.rb +10 -0
- data/lib/redmine_mattermost/issue_patch.rb +33 -0
- data/lib/redmine_mattermost/listener.rb +269 -0
- data/lib/redmine_mattermost/redmine_plugin.rb +48 -0
- data/lib/redmine_mattermost/version.rb +3 -0
- data/redmine-mattermost.gemspec +26 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/Gemfile
ADDED
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.
|
data/README.md
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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,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("&", "&").gsub("<", "<").gsub(">", ">")
|
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,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: []
|