Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f44366bd75 | ||
|
|
7339e9e382 | ||
|
|
4d447f75ab | ||
|
|
593d7055e0 | ||
|
|
c16dec39e4 | ||
|
|
0ee9fa36e6 | ||
|
|
95a914f1ae | ||
|
|
d683f08eef | ||
|
|
1085b042e5 | ||
|
|
454437cbf5 | ||
|
|
a53fd43ef4 | ||
|
|
7a3d335065 | ||
|
|
b2d71d2a0e | ||
|
|
2a1672f024 | ||
|
|
2c3b503f68 | ||
|
|
0427b97a46 | ||
|
|
bbd6a69f8d | ||
|
|
763cf347f6 | ||
|
|
3687fbbe90 | ||
|
|
9a8295247c | ||
|
|
fe8d1fd856 | ||
|
|
53e75212cb | ||
|
|
c98e4fc989 | ||
|
|
18a2856aa2 | ||
|
|
df46bf0f15 | ||
|
|
5d6495f009 | ||
|
|
8162eeb01d | ||
|
|
7e9a994c03 | ||
|
|
2e2e2c4286 | ||
|
|
5882bad2cb | ||
|
|
92f0c40b50 | ||
|
|
a91795a818 | ||
|
|
6295fb3b26 | ||
|
|
a68e3a2fb1 | ||
|
|
b37759d850 | ||
|
|
19f7c9cd05 | ||
|
|
cb8852d95c | ||
|
|
56a3eef324 | ||
|
|
87869706a6 | ||
|
|
22ccab7e72 | ||
|
|
7f1ac1ff38 | ||
|
|
26d2e3978f | ||
|
|
b783970da5 | ||
|
|
5689393e8d | ||
|
|
b03c15586b | ||
|
|
36fb2bb786 | ||
|
|
86ecd0676c | ||
|
|
35798e451c | ||
|
|
19dd1712db | ||
|
|
f1257faf83 | ||
|
|
abbf20b5bd | ||
|
|
4775c2ac49 | ||
|
|
a567405682 | ||
|
|
0672f3808d | ||
|
|
2c0211310b | ||
|
|
b852a33598 | ||
|
|
3116fa6953 | ||
|
|
1a0a087477 | ||
|
|
d8010c6ff0 | ||
|
|
69a4070dcb | ||
|
|
f25dda021e | ||
|
|
28eebbbc80 | ||
|
|
ca69cfd67f | ||
|
|
715ac9f753 |
17
.eslintrc.yml
Normal file
17
.eslintrc.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
env:
|
||||
browser: true
|
||||
jquery: true
|
||||
extends: 'eslint:recommended'
|
||||
rules:
|
||||
indent:
|
||||
- error
|
||||
- 2
|
||||
linebreak-style:
|
||||
- error
|
||||
- unix
|
||||
quotes:
|
||||
- error
|
||||
- single
|
||||
semi:
|
||||
- error
|
||||
- always
|
||||
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
.DS_Store
|
||||
coverage/
|
||||
.buildpath
|
||||
.project
|
||||
.settings/
|
||||
docs/_build
|
||||
docs/_static
|
||||
docs/_templates
|
||||
42
.rubocop.yml
Normal file
42
.rubocop.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
Rails:
|
||||
Enabled: true
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.1
|
||||
TargetRailsVersion: 4.2
|
||||
|
||||
Metrics/AbcSize:
|
||||
Max: 65
|
||||
|
||||
Metrics/BlockLength:
|
||||
Max: 50
|
||||
|
||||
Metrics/ClassLength:
|
||||
Max: 300
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 13
|
||||
|
||||
Metrics/LineLength:
|
||||
Max: 140
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 60
|
||||
|
||||
Metrics/ModuleLength:
|
||||
Max: 300
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 14
|
||||
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
|
||||
Rails/CreateTableWithTimestamps:
|
||||
Enabled: false
|
||||
|
||||
Style/AutoResourceCleanup:
|
||||
Enabled: true
|
||||
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
3
.slim-lint.yml
Normal file
3
.slim-lint.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
linters:
|
||||
LineLength:
|
||||
max: 140
|
||||
163
.stylelintrc.json
Normal file
163
.stylelintrc.json
Normal file
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"rules": {
|
||||
"at-rule-no-unknown": true,
|
||||
"block-no-empty": true,
|
||||
"color-no-invalid-hex": true,
|
||||
"comment-no-empty": true,
|
||||
"declaration-block-no-duplicate-properties": [
|
||||
true,
|
||||
{
|
||||
"ignore": [
|
||||
"consecutive-duplicates-with-different-values"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declaration-block-no-redundant-longhand-properties": true,
|
||||
"declaration-block-no-shorthand-property-overrides": true,
|
||||
"font-family-no-duplicate-names": true,
|
||||
"font-family-no-missing-generic-family-keyword": true,
|
||||
"function-calc-no-unspaced-operator": true,
|
||||
"function-linear-gradient-no-nonstandard-direction": true,
|
||||
"keyframe-declaration-no-important": true,
|
||||
"media-feature-name-no-unknown": true,
|
||||
"no-descending-specificity": true,
|
||||
"no-duplicate-at-import-rules": true,
|
||||
"no-duplicate-selectors": true,
|
||||
"no-empty-source": true,
|
||||
"no-extra-semicolons": true,
|
||||
"no-invalid-double-slash-comments": true,
|
||||
"property-no-unknown": true,
|
||||
"selector-pseudo-class-no-unknown": true,
|
||||
"selector-pseudo-element-no-unknown": true,
|
||||
"selector-type-no-unknown": true,
|
||||
"string-no-newline": true,
|
||||
"unit-no-unknown": true,
|
||||
"at-rule-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"except": [
|
||||
"blockless-after-same-name-blockless",
|
||||
"first-nested"
|
||||
],
|
||||
"ignore": [
|
||||
"after-comment"
|
||||
]
|
||||
}
|
||||
],
|
||||
"at-rule-name-case": "lower",
|
||||
"at-rule-name-space-after": "always-single-line",
|
||||
"at-rule-semicolon-newline-after": "always",
|
||||
"block-closing-brace-empty-line-before": "never",
|
||||
"block-closing-brace-newline-after": "always",
|
||||
"block-closing-brace-newline-before": "always-multi-line",
|
||||
"block-closing-brace-space-before": "always-single-line",
|
||||
"block-opening-brace-newline-after": "always-multi-line",
|
||||
"block-opening-brace-space-after": "always-single-line",
|
||||
"block-opening-brace-space-before": "always",
|
||||
"color-hex-case": "lower",
|
||||
"color-hex-length": "short",
|
||||
"comment-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"except": [
|
||||
"first-nested"
|
||||
],
|
||||
"ignore": [
|
||||
"stylelint-commands"
|
||||
]
|
||||
}
|
||||
],
|
||||
"comment-whitespace-inside": "always",
|
||||
"custom-property-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"except": [
|
||||
"after-custom-property",
|
||||
"first-nested"
|
||||
],
|
||||
"ignore": [
|
||||
"after-comment",
|
||||
"inside-single-line-block"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declaration-bang-space-after": "never",
|
||||
"declaration-bang-space-before": "always",
|
||||
"declaration-block-semicolon-newline-after": "always-multi-line",
|
||||
"declaration-block-semicolon-space-after": "always-single-line",
|
||||
"declaration-block-semicolon-space-before": "never",
|
||||
"declaration-block-single-line-max-declarations": 1,
|
||||
"declaration-block-trailing-semicolon": "always",
|
||||
"declaration-colon-newline-after": "always-multi-line",
|
||||
"declaration-colon-space-after": "always-single-line",
|
||||
"declaration-colon-space-before": "never",
|
||||
"declaration-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"except": [
|
||||
"after-declaration",
|
||||
"first-nested"
|
||||
],
|
||||
"ignore": [
|
||||
"after-comment",
|
||||
"inside-single-line-block"
|
||||
]
|
||||
}
|
||||
],
|
||||
"function-comma-newline-after": "always-multi-line",
|
||||
"function-comma-space-after": "always-single-line",
|
||||
"function-comma-space-before": "never",
|
||||
"function-max-empty-lines": 0,
|
||||
"function-name-case": "lower",
|
||||
"function-parentheses-newline-inside": "always-multi-line",
|
||||
"function-parentheses-space-inside": "never-single-line",
|
||||
"function-whitespace-after": "always",
|
||||
"indentation": 2,
|
||||
"length-zero-no-unit": true,
|
||||
"max-empty-lines": 1,
|
||||
"media-feature-colon-space-after": "always",
|
||||
"media-feature-colon-space-before": "never",
|
||||
"media-feature-name-case": "lower",
|
||||
"media-feature-parentheses-space-inside": "never",
|
||||
"media-feature-range-operator-space-after": "always",
|
||||
"media-feature-range-operator-space-before": "always",
|
||||
"media-query-list-comma-newline-after": "always-multi-line",
|
||||
"media-query-list-comma-space-after": "always-single-line",
|
||||
"media-query-list-comma-space-before": "never",
|
||||
"no-eol-whitespace": true,
|
||||
"no-missing-end-of-source-newline": true,
|
||||
"number-leading-zero": "always",
|
||||
"number-no-trailing-zeros": true,
|
||||
"property-case": "lower",
|
||||
"rule-empty-line-before": [
|
||||
"always-multi-line",
|
||||
{
|
||||
"except": [
|
||||
"first-nested"
|
||||
],
|
||||
"ignore": [
|
||||
"after-comment"
|
||||
]
|
||||
}
|
||||
],
|
||||
"selector-attribute-brackets-space-inside": "never",
|
||||
"selector-attribute-operator-space-after": "never",
|
||||
"selector-attribute-operator-space-before": "never",
|
||||
"selector-combinator-space-after": "always",
|
||||
"selector-combinator-space-before": "always",
|
||||
"selector-descendant-combinator-no-non-space": true,
|
||||
"selector-list-comma-newline-after": "always",
|
||||
"selector-list-comma-space-before": "never",
|
||||
"selector-max-empty-lines": 0,
|
||||
"selector-pseudo-class-case": "lower",
|
||||
"selector-pseudo-class-parentheses-space-inside": "never",
|
||||
"selector-pseudo-element-case": "lower",
|
||||
"selector-pseudo-element-colon-notation": "double",
|
||||
"selector-type-case": "lower",
|
||||
"unit-case": "lower",
|
||||
"value-list-comma-newline-after": "always-multi-line",
|
||||
"value-list-comma-space-after": "always-single-line",
|
||||
"value-list-comma-space-before": "never",
|
||||
"value-list-max-empty-lines": 0
|
||||
}
|
||||
}
|
||||
62
CHANGELOG.md
Normal file
62
CHANGELOG.md
Normal file
@@ -0,0 +1,62 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
1.0.2
|
||||
-----
|
||||
|
||||
- Bug fixed with issue urls, if Redmine is in subdirectory
|
||||
- slim is used as template engine
|
||||
- add private contacts, db and passwords support (if plugins are installed)
|
||||
- Discord support added to documentation
|
||||
|
||||
1.0.1
|
||||
-----
|
||||
|
||||
- Japanese translation has been added - thanks @Yoto
|
||||
- Default mentions has been added - thanks @xstasi
|
||||
|
||||
1.0.0
|
||||
-----
|
||||
|
||||
- Redmine 3.4.x compatibility
|
||||
- Commit message issue bug fix
|
||||
- Some code cleanups
|
||||
|
||||
0.9.9
|
||||
-----
|
||||
|
||||
- All global messenger settings can be overwritten project based
|
||||
- Locale support added
|
||||
- Wiki added supported for notification
|
||||
- Contact added/updated supported for notification (if redmine_contacts is installed)
|
||||
- Password added/updated supported for notification (if redmine_passwords is installed)
|
||||
- DB entry added/updated supported for notification (if redmine_db is installed)
|
||||
- SSL verify can be disabled
|
||||
- Lots of refactoring and code cleanups
|
||||
- Swith from httpclient to net/http
|
||||
- Fork of redmine_rocketchat, redmine_slack and redmine_mattermost (base functions for all three messenger)
|
||||
|
||||
v0.6.1
|
||||
------
|
||||
|
||||
unknown changes
|
||||
|
||||
v0.4
|
||||
----
|
||||
|
||||
unknown changes
|
||||
|
||||
v0.3
|
||||
----
|
||||
|
||||
unknown changes
|
||||
|
||||
v0.2
|
||||
----
|
||||
|
||||
unknown changes
|
||||
|
||||
v0.1
|
||||
----
|
||||
|
||||
unknown changes
|
||||
1
LICENSE
1
LICENSE
@@ -2,6 +2,7 @@ Released under the MIT license.
|
||||
|
||||
Project redmine-slack is Copyright (c) 2014-2016 by Samuel Cormier-Iijima (https://github.com/sciyoshi/redmine-slack).
|
||||
Fork project redmine_mattermost is Copyright (c) 2015-2016 by AltSol (https://github.com/altsol/redmine_mattermost).
|
||||
Fork project redmine_rocketchat is Copyright (c) 2016-2017 by Phlegx (https://github.com/phlegx/redmine_rocketchat).
|
||||
|
||||
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:
|
||||
|
||||
|
||||
124
README.md
124
README.md
@@ -1,68 +1,102 @@
|
||||
# Rocket.Chat plugin for Redmine
|
||||
Messenger plugin for Redmine
|
||||
============================
|
||||
|
||||
This plugin posts updates to issues in your Redmine installation to a Rocket.Chat
|
||||
channel.
|
||||
This plugin posts updates to issues in your Redmine installation to [Slack](https://slack.com/), [Rocket.Chat](https://rocket.chat/), [Discord](https://discordapp.com/) or [Mattermost](https://about.mattermost.com/) channel.
|
||||
|
||||
Redmine Supported versions: 2.0.x - 3.3.x.
|
||||
[](https://gemnasium.com/github.com/AlphaNodes/redmine_messenger) 
|
||||
|
||||
## Screenshot
|
||||
Features
|
||||
--------
|
||||
|
||||

|
||||
* Post information to messenger channel
|
||||
* post issue updates
|
||||
* post private issue updates
|
||||
* display watchers
|
||||
* convert username to mentions
|
||||
* post wiki updates
|
||||
* post db entry (if redmine_db is installed) updates
|
||||
* post password (if redmine_passwords is installed) updates
|
||||
* post contact (if redmine_contacts is installed) updates
|
||||
* overwrite messenger settings at project level
|
||||
* parent project support (inherit messenger settings from parent project)
|
||||
* multiple channel support (define one or more channels to deliver note)
|
||||
|
||||
## Installation
|
||||
Screenshot
|
||||
----------
|
||||
|
||||
From your Redmine plugins directory, clone this repository as `redmine_rocketchat` (note
|
||||
the underscore!):
|
||||
Mattermost output:
|
||||
|
||||
git clone https://github.com/phlegx/redmine_rocketchat.git redmine_rocketchat
|
||||

|
||||
|
||||
You will also need the `httpclient` dependency, which can be installed by running
|
||||
Redmine configuration:
|
||||
|
||||
bundle install
|
||||

|
||||
|
||||
from the plugin directory.
|
||||
Prepare your messenger service
|
||||
------------------------------
|
||||
|
||||
Restart Redmine, and you should see the plugin show up in the Plugins page.
|
||||
Under the configuration options, set the Rocket.Chat API URL to the URL for an
|
||||
Incoming WebHook integration in your Rocket.Chat account and also set the Rocket.Chat
|
||||
### Slack
|
||||
|
||||
Go to Slack documentation [Incoming Webhooks](https://api.slack.com/incoming-webhooks) for more information to set up Incoming WebHook
|
||||
|
||||
### Mattermost
|
||||
|
||||
Go to Mattermost documentation [Incoming Webhooks](https://docs.mattermost.com/developer/webhooks-incoming.html) for more information to set up Incoming WebHook
|
||||
|
||||
### Discord
|
||||
|
||||
Go to Discord documentation [Intro to Webhooks ](https://support.discordapp.com/hc/en-us/articles/228383668) for more information to set up Incoming WebHook
|
||||
You have to add /slack after your webhook url.
|
||||
|
||||
### Rocket.Chat
|
||||
|
||||
Go to Rocket.Chat documentation [Incoming WebHook Scripting](https://rocket.chat/docs/administrator-guides/integrations/) for more information to set up Incoming WebHook
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* Redmine version >= 3.0.0
|
||||
* Ruby version >= 2.1.5
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install ``redmine_messenger`` plugin for `Redmine`
|
||||
|
||||
cd $REDMINE_ROOT
|
||||
git clone git://github.com/alphanodes/redmine_messenger.git plugins/redmine_messenger
|
||||
bundle install --without development test
|
||||
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
|
||||
|
||||
Restart Redmine (application server) and you should see the plugin show up in the Plugins page.
|
||||
Under the configuration options, set the Messenger API URL to the URL for an
|
||||
Incoming WebHook integration in your Messenger account and also set the Messenger
|
||||
Channel to the channel's handle (be careful, this is not the channel's display name
|
||||
visible to users, you can find each channel's handle by navigating inside the channel
|
||||
and clicking the down-arrow and selecting view info). See also the next two sections
|
||||
for advanced and custom routing options.
|
||||
and clicking the down-arrow and selecting view info).
|
||||
|
||||
## Customized Routing
|
||||
|
||||
You can also route messages to different channels on a per-project basis. To
|
||||
do this, create a project custom field (Administration > Custom fields > Project)
|
||||
named `Rocket.Chat Channel`. If no custom channel is defined for a project, the parent
|
||||
project will be checked (or the default will be used). To prevent all notifications
|
||||
from being sent for a project, set the custom channel to `-`.
|
||||
Uninstall
|
||||
---------
|
||||
|
||||
For more information, see http://www.redmine.org/projects/redmine/wiki/Plugins (see also next section for an easy configuration demonstration).
|
||||
Uninstall ``redmine_messenger``
|
||||
|
||||
## Screenshot Guided Configuration
|
||||
cd $REDMINE_ROOT
|
||||
bundle exec rake redmine:plugins:migrate NAME=redmine_messenger VERSION=0 RAILS_ENV=production
|
||||
rm -rf plugins/redmine_messenger
|
||||
|
||||
Step 1: Create an Incoming Webhook in Rocket.Chat (Administration > Integrations > Incoming WebHook).
|
||||
Restart Redmine (application server)
|
||||
|
||||

|
||||
|
||||
Step 2: Install this Redmine plugin for Rocket.Chat.
|
||||
Credits
|
||||
-------
|
||||
|
||||

|
||||
The source code is forked from
|
||||
|
||||
Step 3: Configure this Redmine plugin for Rocket.Chat. For per-project customized routing, leave the `Rocket.Chat Channel` field empty and follow the next steps, otherwise all Redmine projects will post to the same Rocket.Chat 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.
|
||||
- [redmine_rocketchat](https://github.com/phlegx/redmine_rocketchat)
|
||||
- [redmine_mattermost](https://github.com/altsol/redmine_mattermost)
|
||||
- [redmine-slack](https://github.com/sciyoshi/redmine-slack)
|
||||
|
||||

|
||||
|
||||
Step 4: For per-project customized routing, first create the project custom field (Administration > Custom fields > New custom field > Projects).
|
||||
|
||||

|
||||

|
||||
|
||||
Step 5: For per-project customized routing, configure the Rocket.Chat channel handle inside your Redmine project.
|
||||
|
||||

|
||||
|
||||
## Credits
|
||||
|
||||
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 use Rocket.Chat-namespaced configuration options in order to use all hooks (Rocket.Chat, Mattermost and Slack) in a Redmine installation.
|
||||
Special thanks to the original author and contributors for making this awesome hook for Redmine. This fork is just refactored to use Messenger-namespaced configuration options in order to use all hooks for Rocket.Chat, Mattermost AND Slack in a Redmine installation.
|
||||
|
||||
35
app/controllers/messenger_settings_controller.rb
Normal file
35
app/controllers/messenger_settings_controller.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
class MessengerSettingsController < ApplicationController
|
||||
layout 'base'
|
||||
|
||||
before_action :find_project, :authorize, :find_user
|
||||
|
||||
def save
|
||||
setting = MessengerSetting.find_or_create @project.id
|
||||
begin
|
||||
setting.transaction do
|
||||
# setting.auto_preview_enabled = auto_preview_enabled
|
||||
setting.assign_attributes(params[:setting])
|
||||
setting.save!
|
||||
end
|
||||
flash[:notice] = l(:notice_successful_update)
|
||||
rescue => e
|
||||
flash[:error] = 'Updating failed.' + e.message
|
||||
end
|
||||
|
||||
redirect_to controller: 'projects', action: 'settings', id: @project, tab: 'messenger'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_project
|
||||
@project = Project.find(params[:id])
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render_404
|
||||
end
|
||||
|
||||
def find_user
|
||||
@user = User.current
|
||||
end
|
||||
end
|
||||
234
app/models/messenger.rb
Normal file
234
app/models/messenger.rb
Normal file
@@ -0,0 +1,234 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
require 'net/http'
|
||||
|
||||
class Messenger
|
||||
include Redmine::I18n
|
||||
|
||||
def self.default_url_options
|
||||
{ only_path: true, script_name: Redmine::Utils.relative_url_root }
|
||||
end
|
||||
|
||||
def self.speak(msg, channels, url, options)
|
||||
url ||= RedmineMessenger.settings[:messenger_url]
|
||||
|
||||
return if url.blank?
|
||||
return if channels.blank?
|
||||
|
||||
params = {
|
||||
text: msg,
|
||||
link_names: 1
|
||||
}
|
||||
|
||||
username = Messenger.textfield_for_project(options[:project], :messenger_username)
|
||||
params[:username] = username if username.present?
|
||||
params[:attachments] = [options[:attachment]] if options[:attachment] && options[:attachment].any?
|
||||
|
||||
icon = Messenger.textfield_for_project(options[:project], :messenger_icon)
|
||||
if icon.present?
|
||||
if icon.start_with? ':'
|
||||
params[:icon_emoji] = icon
|
||||
else
|
||||
params[:icon_url] = icon
|
||||
end
|
||||
end
|
||||
|
||||
channels.each do |channel|
|
||||
uri = URI(url)
|
||||
params[:channel] = channel
|
||||
http_options = { use_ssl: uri.scheme == 'https' }
|
||||
http_options[:verify_mode] = OpenSSL::SSL::VERIFY_NONE unless RedmineMessenger.setting?(:messenger_verify_ssl)
|
||||
|
||||
begin
|
||||
req = Net::HTTP::Post.new(uri)
|
||||
req.set_form_data(payload: params.to_json)
|
||||
Net::HTTP.start(uri.hostname, uri.port, http_options) do |http|
|
||||
response = http.request(req)
|
||||
Rails.logger.warn(response) unless [Net::HTTPSuccess, Net::HTTPRedirection, Net::HTTPOK].include? response
|
||||
end
|
||||
rescue StandardError => e
|
||||
Rails.logger.warn("cannot connect to #{url}")
|
||||
Rails.logger.warn(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.object_url(obj)
|
||||
if Setting.host_name.to_s =~ %r{\A(https?\://)?(.+?)(\:(\d+))?(/.+)?\z}i
|
||||
host = Regexp.last_match(2)
|
||||
port = Regexp.last_match(4)
|
||||
prefix = Regexp.last_match(5)
|
||||
Rails.application.routes.url_for(obj.event_url(host: host, protocol: Setting.protocol, port: port, script_name: prefix))
|
||||
else
|
||||
Rails.application.routes.url_for(obj.event_url(host: Setting.host_name, protocol: Setting.protocol, script_name: ''))
|
||||
end
|
||||
end
|
||||
|
||||
def self.url_for_project(proj)
|
||||
return if proj.blank?
|
||||
# project based
|
||||
pm = MessengerSetting.find_by(project_id: proj.id)
|
||||
return pm.messenger_url if !pm.nil? && pm.messenger_url.present?
|
||||
# parent project based
|
||||
parent_url = url_for_project(proj.parent)
|
||||
return parent_url if parent_url.present?
|
||||
# system based
|
||||
return RedmineMessenger.settings[:messenger_url] if RedmineMessenger.settings[:messenger_url].present?
|
||||
nil
|
||||
end
|
||||
|
||||
def self.textfield_for_project(proj, config)
|
||||
return if proj.blank?
|
||||
# project based
|
||||
pm = MessengerSetting.find_by(project_id: proj.id)
|
||||
return pm.send(config) if !pm.nil? && pm.send(config).present?
|
||||
default_textfield(proj, config)
|
||||
end
|
||||
|
||||
def self.default_textfield(proj, config)
|
||||
# parent project based
|
||||
parent_field = textfield_for_project(proj.parent, config)
|
||||
return parent_field if parent_field.present?
|
||||
return RedmineMessenger.settings[config] if RedmineMessenger.settings[config].present?
|
||||
''
|
||||
end
|
||||
|
||||
def self.channels_for_project(proj)
|
||||
return [] if proj.blank?
|
||||
# project based
|
||||
pm = MessengerSetting.find_by(project_id: proj.id)
|
||||
if !pm.nil? && pm.messenger_channel.present?
|
||||
return [] if pm.messenger_channel == '-'
|
||||
return pm.messenger_channel.split(',').map(&:strip).uniq
|
||||
end
|
||||
default_project_channels(proj)
|
||||
end
|
||||
|
||||
def self.default_project_channels(proj)
|
||||
# parent project based
|
||||
parent_channel = channels_for_project(proj.parent)
|
||||
return parent_channel if parent_channel.present?
|
||||
# system based
|
||||
if RedmineMessenger.settings[:messenger_channel].present? &&
|
||||
RedmineMessenger.settings[:messenger_channel] != '-'
|
||||
return RedmineMessenger.settings[:messenger_channel].split(',').map(&:strip).uniq
|
||||
end
|
||||
[]
|
||||
end
|
||||
|
||||
def self.setting_for_project(proj, config)
|
||||
return false if proj.blank?
|
||||
@setting_found = 0
|
||||
# project based
|
||||
pm = MessengerSetting.find_by(project_id: proj.id)
|
||||
unless pm.nil? || pm.send(config).zero?
|
||||
@setting_found = 1
|
||||
return false if pm.send(config) == 1
|
||||
return true if pm.send(config) == 2
|
||||
# 0 = use system based settings
|
||||
end
|
||||
default_project_setting(proj, config)
|
||||
end
|
||||
|
||||
def self.default_project_setting(proj, config)
|
||||
if proj.present? && proj.parent.present?
|
||||
parent_setting = setting_for_project(proj.parent, config)
|
||||
return parent_setting if @setting_found == 1
|
||||
end
|
||||
# system based
|
||||
return true if RedmineMessenger.settings[config].present? && RedmineMessenger.setting?(config)
|
||||
false
|
||||
end
|
||||
|
||||
def self.detail_to_field(detail)
|
||||
field_format = nil
|
||||
key = nil
|
||||
escape = true
|
||||
|
||||
if detail.property == 'cf'
|
||||
key = CustomField.find(detail.prop_key).name rescue nil
|
||||
title = key
|
||||
field_format = CustomField.find(detail.prop_key).field_format rescue nil
|
||||
elsif detail.property == 'attachment'
|
||||
key = 'attachment'
|
||||
title = I18n.t :label_attachment
|
||||
else
|
||||
key = detail.prop_key.to_s.sub('_id', '')
|
||||
title = if key == 'parent'
|
||||
I18n.t "field_#{key}_issue"
|
||||
else
|
||||
I18n.t "field_#{key}"
|
||||
end
|
||||
end
|
||||
|
||||
short = true
|
||||
value = detail.value.to_s
|
||||
|
||||
case key
|
||||
when 'title', 'subject', 'description'
|
||||
short = false
|
||||
when 'tracker'
|
||||
tracker = Tracker.find(detail.value)
|
||||
value = tracker.to_s if tracker.present?
|
||||
when 'project'
|
||||
project = Project.find(detail.value)
|
||||
value = project.to_s if project.present?
|
||||
when 'status'
|
||||
status = IssueStatus.find(detail.value)
|
||||
value = status.to_s if status.present?
|
||||
when 'priority'
|
||||
priority = IssuePriority.find(detail.value)
|
||||
value = priority.to_s if priority.present?
|
||||
when 'category'
|
||||
category = IssueCategory.find(detail.value)
|
||||
value = category.to_s if category.present?
|
||||
when 'assigned_to'
|
||||
user = User.find(detail.value)
|
||||
value = user.to_s if user.present?
|
||||
when 'fixed_version'
|
||||
fixed_version = Version.find(detail.value)
|
||||
value = fixed_version.to_s if fixed_version.present?
|
||||
when 'attachment'
|
||||
attachment = Attachment.find(detail.prop_key)
|
||||
value = "<#{Messenger.object_url attachment}|#{ERB::Util.html_escape(attachment.filename)}>" if attachment.present?
|
||||
escape = false
|
||||
when 'parent'
|
||||
issue = Issue.find(detail.value)
|
||||
value = "<#{Messenger.object_url issue}|#{ERB::Util.html_escape(issue)}>" if issue.present?
|
||||
escape = false
|
||||
end
|
||||
|
||||
if detail.property == 'cf' && field_format == 'version'
|
||||
version = Version.find(detail.value)
|
||||
value = version.to_s if version.present?
|
||||
end
|
||||
|
||||
value = if value.present?
|
||||
if escape
|
||||
ERB::Util.html_escape(value)
|
||||
else
|
||||
value
|
||||
end
|
||||
else
|
||||
'-'
|
||||
end
|
||||
|
||||
result = { title: title, value: value }
|
||||
result[:short] = true if short
|
||||
result
|
||||
end
|
||||
|
||||
def self.mentions(project, text)
|
||||
names = []
|
||||
Messenger.textfield_for_project(project, :default_mentions)
|
||||
.split(',').each { |m| names.push m.strip }
|
||||
names += extract_usernames(text) unless text.nil?
|
||||
names.present? ? ' To: ' + names.uniq.join(', ') : nil
|
||||
end
|
||||
|
||||
def self.extract_usernames(text)
|
||||
text = '' if text.nil?
|
||||
# messenger usernames may only contain lowercase letters, numbers,
|
||||
# dashes, dots and underscores and must start with a letter or number.
|
||||
text.scan(/@[a-z0-9][a-z0-9_\-.]*/).uniq
|
||||
end
|
||||
end
|
||||
45
app/models/messenger_setting.rb
Normal file
45
app/models/messenger_setting.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
class MessengerSetting < ActiveRecord::Base
|
||||
include Redmine::SafeAttributes
|
||||
belongs_to :project
|
||||
|
||||
validates :project_id, presence: true
|
||||
|
||||
safe_attributes 'messenger_url',
|
||||
'messenger_icon',
|
||||
'messenger_channel',
|
||||
'messenger_username',
|
||||
'messenger_verify_ssl',
|
||||
'auto_mentions',
|
||||
'default_mentions',
|
||||
'display_watchers',
|
||||
'post_updates',
|
||||
'new_include_description',
|
||||
'updated_include_description',
|
||||
'post_private_issues',
|
||||
'post_private_notes',
|
||||
'post_wiki',
|
||||
'post_wiki_updates',
|
||||
'post_db',
|
||||
'post_db_updates',
|
||||
'post_private_db',
|
||||
'post_contact',
|
||||
'post_contact_updates',
|
||||
'post_private_contacts',
|
||||
'post_password',
|
||||
'post_password_updates'
|
||||
|
||||
attr_protected :id
|
||||
|
||||
def self.find_or_create(p_id)
|
||||
setting = MessengerSetting.find_by(project_id: p_id)
|
||||
unless setting
|
||||
setting = MessengerSetting.new
|
||||
setting.project_id = p_id
|
||||
setting.save!
|
||||
end
|
||||
|
||||
setting
|
||||
end
|
||||
end
|
||||
7
app/views/messenger_settings/_messenger_select.html.slim
Normal file
7
app/views/messenger_settings/_messenger_select.html.slim
Normal file
@@ -0,0 +1,7 @@
|
||||
p
|
||||
= f.select mf, project_messenger_options(@messenger_setting.send(mf)), label: l("label_settings_#{mf}")
|
||||
'
|
||||
em.info[style="display: inline;"]
|
||||
= l(:label_default)
|
||||
' :
|
||||
= project_setting_messenger_default_value(mf)
|
||||
9
app/views/messenger_settings/_messenger_text.html.slim
Normal file
9
app/views/messenger_settings/_messenger_text.html.slim
Normal file
@@ -0,0 +1,9 @@
|
||||
p
|
||||
= f.text_field mf, size: size, label: l("label_settings_#{mf}")
|
||||
em.info
|
||||
= l(:label_messenger_project_text_field_info)
|
||||
| (
|
||||
= l(:label_default)
|
||||
' :
|
||||
= Messenger.default_textfield(@project, mf)
|
||||
| )
|
||||
71
app/views/messenger_settings/_show.html.slim
Normal file
71
app/views/messenger_settings/_show.html.slim
Normal file
@@ -0,0 +1,71 @@
|
||||
.box.tabular.messenger_settings
|
||||
- @messenger_setting = MessengerSetting.find_or_create(@project.id)
|
||||
= labelled_form_for :setting,
|
||||
@messenger_setting,
|
||||
url: { controller: 'messenger_settings',
|
||||
action: 'save', id: @project, tab: 'messenger',
|
||||
partial: 'messenger_settings/save',
|
||||
setting_id: @messenger_setting.id } do |f|
|
||||
= error_messages_for 'messenger_setting'
|
||||
.box
|
||||
.info = t(:messenger_settings_project_intro)
|
||||
br
|
||||
p
|
||||
= f.text_field :messenger_url, size: 60, label: l(:label_settings_messenger_url)
|
||||
em.info
|
||||
= l(:label_messenger_project_text_field_info)
|
||||
| (
|
||||
= l(:label_messenger_default_not_visible)
|
||||
| )
|
||||
= render partial: 'messenger_settings/messenger_text', locals: { f: f, mf: :messenger_icon, size: 60 }
|
||||
= render partial: 'messenger_settings/messenger_text', locals: { f: f, mf: :messenger_channel, size: 30 }
|
||||
= render partial: 'messenger_settings/messenger_text', locals: { f: f, mf: :messenger_username, size: 30 }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :messenger_verify_ssl }
|
||||
|
||||
br
|
||||
h3 = l(:label_issue_plural)
|
||||
.info = t(:messenger_issue_intro)
|
||||
br
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :auto_mentions }
|
||||
= render partial: 'messenger_settings/messenger_text', locals: { f: f, mf: :default_mentions, size: 30 }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :display_watchers }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_updates }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :new_include_description }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :updated_include_description }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_private_issues }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_private_notes }
|
||||
|
||||
br
|
||||
h3 = l(:label_wiki)
|
||||
.info = t(:messenger_wiki_intro)
|
||||
br
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_wiki }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_wiki_updates }
|
||||
|
||||
- if RedmineMessenger::REDMINE_DB_SUPPORT && User.current.allowed_to?(:view_db_entries, @project)
|
||||
br
|
||||
h3 = l(:label_db_entry_plural)
|
||||
.info = t(:messenger_db_intro)
|
||||
br
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_db }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_db_updates }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_private_db }
|
||||
|
||||
- if RedmineMessenger::REDMINE_CONTACTS_SUPPORT && User.current.allowed_to?(:view_contacts, @project)
|
||||
br
|
||||
h3 = l(:label_contact_plural)
|
||||
.info = t(:messenger_contacts_intro)
|
||||
br
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_contact }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_contact_updates }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_private_contacts }
|
||||
|
||||
- if Redmine::Plugin.installed?('redmine_passwords') && User.current.allowed_to?(:view_passwords, @project)
|
||||
br
|
||||
h3 = l(:label_settings_post_password)
|
||||
.info = t(:messenger_passwords_intro)
|
||||
br
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_password }
|
||||
= render partial: 'messenger_settings/messenger_select', locals: { f: f, mf: :post_password_updates }
|
||||
|
||||
= submit_tag l(:button_save)
|
||||
104
app/views/settings/_messenger_settings.html.slim
Normal file
104
app/views/settings/_messenger_settings.html.slim
Normal file
@@ -0,0 +1,104 @@
|
||||
- @settings = ActionController::Parameters.new(@settings)
|
||||
.info = t(:messenger_settings_intro)
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_messenger_url))
|
||||
= text_field_tag('settings[messenger_url]', @settings[:messenger_url], size: 60, placeholder: 'https://webhook.url')
|
||||
em.info = t(:messenger_url_info_html)
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_messenger_icon))
|
||||
= text_field_tag('settings[messenger_icon]', @settings[:messenger_icon], size: 60)
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_messenger_channel))
|
||||
= text_field_tag('settings[messenger_channel]', @settings[:messenger_channel], size: 30, placeholder: 'redmine')
|
||||
em.info = t(:messenger_channel_info_html)
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_messenger_username))
|
||||
= text_field_tag('settings[messenger_username]', @settings[:messenger_username], size: 30)
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_messenger_verify_ssl))
|
||||
= check_box_tag 'settings[messenger_verify_ssl]', 1, @settings[:messenger_verify_ssl].to_i == 1
|
||||
em.info = t(:messenger_verify_ssl_info_html)
|
||||
|
||||
br
|
||||
h3 = l(:label_issue_plural)
|
||||
.info = t(:messenger_issue_intro)
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_auto_mentions))
|
||||
= check_box_tag 'settings[auto_mentions]', 1, @settings[:auto_mentions].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_default_mentions))
|
||||
= text_field_tag('settings[default_mentions]', @settings[:default_mentions], size: 30)
|
||||
em.info = t(:default_mentionsl_info)
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_display_watchers))
|
||||
= check_box_tag 'settings[display_watchers]', 1, @settings[:display_watchers].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_updates))
|
||||
= check_box_tag 'settings[post_updates]', 1, @settings[:post_updates].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_new_include_description))
|
||||
= check_box_tag 'settings[new_include_description]', 1, @settings[:new_include_description].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_updated_include_description))
|
||||
= check_box_tag 'settings[updated_include_description]', 1, @settings[:updated_include_description].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_private_issues))
|
||||
= check_box_tag 'settings[post_private_issues]', 1, @settings[:post_private_issues].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_private_notes))
|
||||
= check_box_tag 'settings[post_private_notes]', 1, @settings[:post_private_notes].to_i == 1
|
||||
|
||||
br
|
||||
h3 = l(:label_wiki)
|
||||
.info = t(:messenger_wiki_intro)
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_wiki))
|
||||
= check_box_tag 'settings[post_wiki]', 1, @settings[:post_wiki].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_wiki_updates))
|
||||
= check_box_tag 'settings[post_wiki_updates]', 1, @settings[:post_wiki_updates].to_i == 1
|
||||
|
||||
- if RedmineMessenger::REDMINE_DB_SUPPORT
|
||||
br
|
||||
h3 = l(:label_db_entry_plural)
|
||||
.info = t(:messenger_db_intro)
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_db))
|
||||
= check_box_tag 'settings[post_db]', 1, @settings[:post_db].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_db_updates))
|
||||
= check_box_tag 'settings[post_db_updates]', 1, @settings[:post_db_updates].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_private_db))
|
||||
= check_box_tag 'settings[post_private_db]', 1, @settings[:post_private_db].to_i == 1
|
||||
|
||||
- if RedmineMessenger::REDMINE_CONTACTS_SUPPORT
|
||||
br
|
||||
h3 = l(:label_contact_plural)
|
||||
.info = t(:messenger_contacts_intro)
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_contact))
|
||||
= check_box_tag 'settings[post_contact]', 1, @settings[:post_contact].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_contact_updates))
|
||||
= check_box_tag 'settings[post_contact_updates]', 1, @settings[:post_contact_updates].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_private_contacts))
|
||||
= check_box_tag 'settings[post_private_contacts]', 1, @settings[:post_private_contacts].to_i == 1
|
||||
|
||||
- if Redmine::Plugin.installed?('redmine_passwords')
|
||||
br
|
||||
h3 = l(:label_password_plural)
|
||||
.info = t(:messenger_passwords_intro)
|
||||
br
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_password))
|
||||
= check_box_tag 'settings[post_password]', 1, @settings[:post_password].to_i == 1
|
||||
p
|
||||
= content_tag(:label, l(:label_settings_post_password_updates))
|
||||
= check_box_tag 'settings[post_password_updates]', 1, @settings[:post_password_updates].to_i == 1
|
||||
@@ -1,71 +0,0 @@
|
||||
<p>
|
||||
<label for="settings_rocketchat_url">Rocket.Chat URL</label>
|
||||
<input type="text" id="settings_rocketchat_url" size=80 value="<%= settings['rocketchat_url'] %>" name="settings[rocketchat_url]" placeholder="https://rocket.chat/hooks/my_rocket_chat_token" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Generate an "Incoming WebHook" URL from the <a href="https://rocket.chat/docs/administrator-guides/integrations/">Integrations configuration page on Rocket.Chat</a>.
|
||||
This URL can be changed on a per-project basis by creating a <a href="/custom_fields/new?type=ProjectCustomField&custom_field[name]=Rocket.Chat Channel">project custom field</a>
|
||||
named "Rocket.Chat URL" (without quotes).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_channel">Rocket.Chat Channel</label>
|
||||
<input type="text" id="settings_channel" value="<%= settings['channel'] %>" name="settings[channel]" placeholder="redmine" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The channel can be changed on a per-project basis by creating a
|
||||
<a href="/custom_fields/new?type=ProjectCustomField&custom_field[name]=Rocket.Chat Channel">project custom field</a>
|
||||
named "Rocket.Chat Channel" (without quotes).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_icon">Rocket.Chat Icon</label>
|
||||
<input type="text" id="settings_icon" value="<%= settings['icon'] %>" name="settings[icon]" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_username">Rocket.Chat Username</label>
|
||||
<input type="text" id="settings_username" value="<%= settings['username'] %>" name="settings[username]" placeholder="redmine.bot" />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_auto_mentions">Display watchers?</label>
|
||||
<input type="checkbox" id="settings_display_watchers" value="1" name="settings[display_watchers]" <%= settings['display_watchers'] == '1' ? 'checked="checked"' : '' %> />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_auto_mentions">Convert names to mentions?</label>
|
||||
<input type="checkbox" id="settings_auto_mentions" value="1" name="settings[auto_mentions]" <%= settings['auto_mentions'] == '1' ? 'checked="checked"' : '' %> />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_post_updates">Post issue updates?</label>
|
||||
<input type="checkbox" id="settings_post_updates" value="1" name="settings[post_updates]" <%= settings['post_updates'] == '1' ? 'checked="checked"' : '' %> />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_new_include_description">Description in new issue?</label>
|
||||
<input type="checkbox" id="settings_new_include_description" value="1" name="settings[new_include_description]" <%= settings['new_include_description'] == '1' ? 'checked="checked"' : '' %> />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_updated_include_description">Description in update issue?</label>
|
||||
<input type="checkbox" id="settings_updated_include_description" value="1" name="settings[updated_include_description]" <%= settings['updated_include_description'] == '1' ? 'checked="checked"' : '' %> />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_post_updates">Post updates for private issues?</label>
|
||||
<input type="checkbox" id="settings_post_private_issues" value="1" name="settings[post_private_issues]" <%= settings['post_private_issues'] == '1' ? 'checked="checked"' : '' %> />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_post_updates">Post private notes?</label>
|
||||
<input type="checkbox" id="settings_post_private_notes" value="1" name="settings[post_private_notes]" <%= settings['post_private_notes'] == '1' ? 'checked="checked"' : '' %> />
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="settings_post_wiki_updates">Post Wiki updates?</label>
|
||||
<input type="checkbox" id="settings_post_wiki_updates" value="1" name="settings[post_wiki_updates]" <%= settings['post_wiki_updates'] == '1' ? 'checked="checked"' : '' %> />
|
||||
</p>
|
||||
BIN
assets/images/icon.png
Normal file
BIN
assets/images/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
assets/images/screenshot_mattermost.png
Normal file
BIN
assets/images/screenshot_mattermost.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
BIN
assets/images/screenshot_redmine_settings.png
Normal file
BIN
assets/images/screenshot_redmine_settings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 214 KiB |
1
assets/javascripts/.eslintignore
Normal file
1
assets/javascripts/.eslintignore
Normal file
@@ -0,0 +1 @@
|
||||
# eslint ignore file
|
||||
1
assets/stylesheets/.stylelintignore
Normal file
1
assets/stylesheets/.stylelintignore
Normal file
@@ -0,0 +1 @@
|
||||
# stylelint ignore file
|
||||
54
config/locales/de.yml
Normal file
54
config/locales/de.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
# German strings
|
||||
de:
|
||||
permission_manage_messenger: Messenger verwalten
|
||||
label_messenger_contact_created: "[%{project_url}] Kontakt %{url} erstellt von *%{user}*"
|
||||
label_messenger_contact_updated: "[%{project_url}] Kontakt %{url} aktualisiert von *%{user}*"
|
||||
label_messenger_db_entry_created: "[%{project_url}] DB Eintrag %{url} erstellt von *%{user}*"
|
||||
label_messenger_db_entry_updated: "[%{project_url}] DB Eintrag %{url} aktualisiert von *%{user}*"
|
||||
label_messenger_default_not_visible: Standardeinstellung wird aus Sicherheitsgründen nicht angezeigt
|
||||
label_messenger_issue_created: "[%{project_url}] Ticket %{url} erstellt von *%{user}*"
|
||||
label_messenger_issue_updated: "[%{project_url}] Ticket %{url} aktualisiert von *%{user}*"
|
||||
label_messenger_password_created: "[%{project_url}] Kennwort %{url} erstellt von *%{user}*"
|
||||
label_messenger_password_updated: "[%{project_url}] Kennwort %{url} aktualisiert von *%{user}*"
|
||||
label_messenger_project_text_field_info: Leer lassen für Systemstandard.
|
||||
label_messenger_settings_default: Standardeinstellung
|
||||
label_messenger_settings_disabled: Deaktiviert
|
||||
label_messenger_settings_enabled: Aktiviert
|
||||
label_messenger_wiki_created: "[%{project_url}] Wiki %{url} erstellt von *%{user}*"
|
||||
label_messenger_wiki_updated: "[%{project_url}] Wiki %{url} aktualisiert von *%{user}*"
|
||||
label_messenger: Messenger
|
||||
label_settings_auto_mentions: Namen für Mentions konvertiert?
|
||||
label_settings_default_mentions: Standardbenutzer für Mentions
|
||||
label_settings_display_watchers: Beobachter?
|
||||
label_settings_messenger_channel: Messenger Channel
|
||||
label_settings_messenger_icon: Messenger Icon
|
||||
label_settings_messenger_url: Messenger URL
|
||||
label_settings_messenger_username: Messenger Benutzer
|
||||
label_settings_messenger_verify_ssl: SSL verifiziert
|
||||
label_settings_new_include_description: Neue Ticketeschreibung?
|
||||
label_settings_post_contact_updates: Kontakt Updates?
|
||||
label_settings_post_contact: Neue Kontakte?
|
||||
label_settings_post_db_updates: DB Einträge Updates?
|
||||
label_settings_post_db: Neue DB Einträge?
|
||||
label_settings_post_password_updates: Passwort Updates?
|
||||
label_settings_post_password: Neue Passwörter?
|
||||
label_settings_post_private_db: Private DB Einträge?
|
||||
label_settings_post_private_contacts: Private Kontakte?
|
||||
label_settings_post_private_issues: Private Ticket Updates?
|
||||
label_settings_post_private_notes: Private Notizen?
|
||||
label_settings_post_updates: Ticket Updates?
|
||||
label_settings_post_wiki_updates: Wiki Updates?
|
||||
label_settings_post_wiki: Neue Wikis?
|
||||
label_settings_updated_include_description: Ticket Beschreibungsupdates?
|
||||
messenger_channel_info_html: 'Hier wird der Channel dem die Nachrichten gesendet werden sollen. Es können mehrere Channel angegeben werden, indem Sie mit Komma getrennt werden.'
|
||||
messenger_contacts_intro: Legen Sie fest welche Einträge oder Änderungen für Kontakte im angegebenen Messenger Channel versendet werden.
|
||||
messenger_db_intro: Legen Sie fest welche Einträge oder Änderungen für DB-Einträge im angegebenen Messenger Channel versendet werden.
|
||||
messenger_issue_intro: Legen Sie fest welche Einträge oder Änderungen für Tickets im angegebenen Messenger Channel versendet werden.
|
||||
messenger_passwords_intro: Legen Sie fest welche Einträge oder Änderungen für Passwörter im angegebenen Messenger Channel versendet werden.
|
||||
messenger_settings_intro: Ist die Messenger URL im Adminbereich leer werden keine Nachrichten von Redmine an den Messenger verschickt. Egal welche Angaben sonst hier durchgeführt werden. Ist der globale Versand von Nachrichten für alle Projekte gewünscht, trägt man hier die URL ein. Für alle Projekte werden dann in den entsprechenden Channel Nachrichten versendet. Will man jedoch nur für bestimmte Projekte etwas schicken lässt man die URL hier leer. Die entsprechenden Einstellungen müssen dann in den jeweiligen Projekten in der Projektkonfiguration durchgeführt werden.
|
||||
messenger_settings_project_intro: Wurde die Messenger URL im Administrationsbereich leer gelassen weil man Änderungen nur für bestimmte Projekte an den Messenger schicken will, trägt man in der jeweiligen Projektkonfiguration die Messenger URL ein.
|
||||
messenger_url_info_html: 'Generiere eine <a target="_blank" href="https://github.com/AlphaNodes/redmine_messenger#prepare-your-messenger-service">Incoming WebHook</a> URL vom Messenger Service. Um nur aus bestimmten Projekten Nachrichten zu versenden, kann diese URL leer bleiben und in den Projekteinstellungen gesetzt werden.'
|
||||
messenger_verify_ssl_info_html: 'Falls der Messenger Service ein ungültiges oder selbst erstelltes (self-signed) SSL Zertifikat verwendet bitte deaktivieren.'
|
||||
messenger_wiki_intro: Legen Sie fest welche Einträge oder Änderungen für Wikis im angegebenen Messenger Channel versendet werden.
|
||||
label_messenger_setting: Messenger Einstellung
|
||||
default_mentionsl_info: Benutzer, die immer mit Mentions benachrichtet werden sollen. Mehrere Namen können mit Komma getrennt angegeben werden (z.B. @all, @here).
|
||||
54
config/locales/en.yml
Normal file
54
config/locales/en.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
# English strings
|
||||
en:
|
||||
permission_manage_messenger: Manage messenger
|
||||
label_messenger_contact_created: "[%{project_url}] Contact %{url} created by *%{user}*"
|
||||
label_messenger_contact_updated: "[%{project_url}] Contact %{url} updated by *%{user}*"
|
||||
label_messenger_db_entry_created: "[%{project_url}] DB entry %{url} created by *%{user}*"
|
||||
label_messenger_db_entry_updated: "[%{project_url}] DB entry %{url} updated by *%{user}*"
|
||||
label_messenger_default_not_visible: Default setting is not visible for security reasons
|
||||
label_messenger_issue_created: "[%{project_url}] Issue %{url} created by *%{user}*"
|
||||
label_messenger_issue_updated: "[%{project_url}] Issue %{url} updated by *%{user}*"
|
||||
label_messenger_password_created: "[%{project_url}] Kennwort %{url} created by *%{user}*"
|
||||
label_messenger_password_updated: "[%{project_url}] Kennwort %{url} updated by *%{user}*"
|
||||
label_messenger_project_text_field_info: Leave it blank for system default.
|
||||
label_messenger_settings_default: System default
|
||||
label_messenger_settings_disabled: Disabled
|
||||
label_messenger_settings_enabled: Enabled
|
||||
label_messenger_wiki_created: "[%{project_url}] Wiki %{url} created by *%{user}*"
|
||||
label_messenger_wiki_updated: "[%{project_url}] Wiki %{url} updated by *%{user}*"
|
||||
label_messenger: Messenger
|
||||
label_settings_auto_mentions: Convert names to mentions?
|
||||
label_settings_default_mentions: Default people for mentions
|
||||
label_settings_display_watchers: Display watchers?
|
||||
label_settings_messenger_channel: Messenger Channel
|
||||
label_settings_messenger_icon: Messenger Icon
|
||||
label_settings_messenger_url: Messenger URL
|
||||
label_settings_messenger_username: Messenger username
|
||||
label_settings_messenger_verify_ssl: Verify SSL
|
||||
label_settings_new_include_description: New issue description?
|
||||
label_settings_post_contact_updates: Contact updates?
|
||||
label_settings_post_contact: Contact added?
|
||||
label_settings_post_db_updates: DB entry updates?
|
||||
label_settings_post_db: DB entry added?
|
||||
label_settings_post_password_updates: Password updates?
|
||||
label_settings_post_password: Password added?
|
||||
label_settings_post_private_db: Private DB entries?
|
||||
label_settings_post_private_contacts: Private contacts?
|
||||
label_settings_post_private_issues: Private issue updates?
|
||||
label_settings_post_private_notes: Private notes updates?
|
||||
label_settings_post_updates: Issue updates?
|
||||
label_settings_post_wiki_updates: Wiki updates?
|
||||
label_settings_post_wiki: Post Wiki added?
|
||||
label_settings_updated_include_description: Description in update issue?
|
||||
messenger_channel_info_html: 'Here you have to specify the channel, which should be used. You can define multible channels, seperated by comma'
|
||||
messenger_contacts_intro: Activate the changes for Issues that should be sent to the pre-defined Messenger channel.
|
||||
messenger_db_intro: Activate the changes for DB that should be sent to the pre-defined Messenger channel.
|
||||
messenger_issue_intro: Activate the changes for Issues that should be sent to the pre-defined Messenger channel.
|
||||
messenger_passwords_intro: Activate the changes for Passwords that should be sent to the pre-defined Messenger channel.
|
||||
messenger_settings_intro: "Leave the Messenger URL in the administration area empty in order to send no messages from Redmine to the Messenger. No matter what else you have configured here. In case you wish to send messages of all Redmine projects to the Messenger fill out the URL. The messages will be sent to the specific channel. If you want only to be notified by some of the projects (not all) leave the messenger URL here blank and go to the specific project configuration."
|
||||
messenger_settings_project_intro: "If you left empty the Messenger URL in the administration area in case to be not globally notified by all project changes you can configure your Messenger URL in the project settings."
|
||||
messenger_url_info_html: 'Generate an <a target="_blank" href="https://github.com/AlphaNodes/redmine_messenger#prepare-your-messenger-service">Incoming WebHook</a> URL from the messenger service. Leave it empty, if you only want to activate specific projects with project based settings'
|
||||
messenger_verify_ssl_info_html: 'If your Messenger service uses an invalid or self-signed SSL certificate, disable it.'
|
||||
messenger_wiki_intro: Activate the changes for Wikis that should be sent to the pre-defined Messenger channel.
|
||||
label_messenger_setting: Messenger Settings
|
||||
default_mentionsl_info: Default people to notify, comma separated (e.g. @all, @here)
|
||||
54
config/locales/ja.yml
Normal file
54
config/locales/ja.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
# Japanese strings
|
||||
ja:
|
||||
permission_manage_messenger: メッセンジャーの管理
|
||||
label_messenger_contact_created: "[%{project_url}] コンタクト %{url} が *%{user}* によって作成されました。"
|
||||
label_messenger_contact_updated: "[%{project_url}] コンタクト %{url} が*%{user}* によって更新されました。"
|
||||
label_messenger_db_entry_created: "[%{project_url}] DB entry %{url} created by *%{user}*"
|
||||
label_messenger_db_entry_updated: "[%{project_url}] DB entry %{url} updated by *%{user}*"
|
||||
label_messenger_default_not_visible: デフォルトの設定はセキュリティ上の理由により表示されません。
|
||||
label_messenger_issue_created: "[%{project_url}] チケット %{url} が *%{user}* によって作成されました。"
|
||||
label_messenger_issue_updated: "[%{project_url}] チケット %{url} が *%{user}* によって更新されました。"
|
||||
label_messenger_password_created: "[%{project_url}] Kennwort %{url} created by *%{user}*"
|
||||
label_messenger_password_updated: "[%{project_url}] Kennwort %{url} updated by *%{user}*"
|
||||
label_messenger_project_text_field_info: デフォルトの設定を使う場合空欄にしてください。
|
||||
label_messenger_settings_default: デフォルト
|
||||
label_messenger_settings_disabled: 無効
|
||||
label_messenger_settings_enabled: 有効
|
||||
label_messenger_wiki_created: "[%{project_url}] Wiki %{url} が by *%{user}* によって作成されました。"
|
||||
label_messenger_wiki_updated: "[%{project_url}] Wiki %{url} が *%{user}* によって更新されました。"
|
||||
label_messenger: メッセンジャー
|
||||
label_settings_auto_mentions: ユーザー名をその人についての投稿(@ユーザー名)に変換する
|
||||
label_settings_default_mentions: Default people for mentions
|
||||
label_settings_display_watchers: ウォッチャーを表示する
|
||||
label_settings_messenger_channel: メッセンジャーのチャンネル
|
||||
label_settings_messenger_icon: メッセンジャーのアイコン
|
||||
label_settings_messenger_url: メッセンジャーのURL
|
||||
label_settings_messenger_username: メッセンジャーのユーザー名
|
||||
label_settings_messenger_verify_ssl: SSL証明書の検証
|
||||
label_settings_new_include_description: チケット作成時に説明を含める
|
||||
label_settings_post_contact_updates: コンタクトの更新
|
||||
label_settings_post_contact: コンタクトの作成
|
||||
label_settings_post_db_updates: DB entry updates?
|
||||
label_settings_post_db: DB entry added?
|
||||
label_settings_post_password_updates: Password updates?
|
||||
label_settings_post_password: Password added?
|
||||
label_settings_post_private_db: Private DB entries?
|
||||
label_settings_post_private_contacts: Private contacts?
|
||||
label_settings_post_private_issues: プライベートチケットの更新
|
||||
label_settings_post_private_notes: プライベート注記の更新
|
||||
label_settings_post_updates: チケットの更新
|
||||
label_settings_post_wiki_updates: Wikiの更新
|
||||
label_settings_post_wiki: Wikiの作成
|
||||
label_settings_updated_include_description: チケット更新時に説明を含める
|
||||
messenger_channel_info_html: チャンネルを指定する必要があります。複数のチャンネルを指定する場合は,(カンマ)で区切って下さい。
|
||||
messenger_contacts_intro: メッセンジャーに送信するチケットのイベントにチェックを入れて下さい。
|
||||
messenger_db_intro: Activate the changes for DB that should be sent to the pre-defined Messenger channel.
|
||||
messenger_issue_intro: メッセンジャーに送信するチケットのイベントにチェックを入れて下さい。
|
||||
messenger_passwords_intro: Activate the changes for Passwords that should be sent to the pre-defined Messenger channel.
|
||||
messenger_settings_intro: 管理画面でメッセンジャーのURLを指定した場合、全てのプロジェクトのデフォルトの送信先になります。プロジェクト毎の送信先を設定する場合はプロジェクトの設定画面で設定して下さい。
|
||||
messenger_settings_project_intro: メッセンジャーのURLを指定しない場合、管理者が指定したチャンネルにメッセージが送信されます。(管理者がチャンネルを指定していない場合は、空欄にすると機能は無効になります)
|
||||
messenger_url_info_html: 'メッセンジャー サービスで <a target="_blank" href="https://github.com/AlphaNodes/redmine_messenger#prepare-your-messenger-service">内向きのウェブフック</a>のURLを作成して下さい。プロジェクト毎に設定を変えたい場合は空欄として下さい。'
|
||||
messenger_verify_ssl_info_html: 'メッセンジャー サービスが自己署名証明書や不正な証明書を使っている場合、無効として下さい。'
|
||||
messenger_wiki_intro: メッセンジャーに送信するWikiのイベントにチェックを入れて下さい。
|
||||
label_messenger_setting: メッセンジャーの設定
|
||||
default_mentionsl_info: Default people to notify, comma separated (e.g. @all, @here)
|
||||
7
config/routes.rb
Normal file
7
config/routes.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
RedmineApp::Application.routes.draw do
|
||||
match 'projects/:id/messenger_settings/save',
|
||||
to: 'messenger_settings#save',
|
||||
via: %i[post put patch]
|
||||
end
|
||||
29
db/migrate/001_create_messenger_settings.rb
Normal file
29
db/migrate/001_create_messenger_settings.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
class CreateMessengerSettings < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :messenger_settings do |t|
|
||||
t.references :project, null: false, index: true
|
||||
t.string :messenger_url
|
||||
t.string :messenger_icon
|
||||
t.string :messenger_channel
|
||||
t.string :messenger_username
|
||||
t.integer :messenger_verify_ssl, default: 0, null: false
|
||||
t.integer :auto_mentions, default: 0, null: false
|
||||
t.integer :display_watchers, default: 0, null: false
|
||||
t.integer :post_updates, default: 0, null: false
|
||||
t.integer :new_include_description, default: 0, null: false
|
||||
t.integer :updated_include_description, default: 0, null: false
|
||||
t.integer :post_private_issues, default: 0, null: false
|
||||
t.integer :post_private_notes, default: 0, null: false
|
||||
t.integer :post_wiki, default: 0, null: false
|
||||
t.integer :post_wiki_updates, default: 0, null: false
|
||||
t.integer :post_db, default: 0, null: false
|
||||
t.integer :post_db_updates, default: 0, null: false
|
||||
t.integer :post_contact, default: 0, null: false
|
||||
t.integer :post_contact_updates, default: 0, null: false
|
||||
t.integer :post_password, default: 0, null: false
|
||||
t.integer :post_password_updates, default: 0, null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
7
db/migrate/002_add_default_mentions.rb
Normal file
7
db/migrate/002_add_default_mentions.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
class AddDefaultMentions < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :messenger_settings, :default_mentions, :string
|
||||
end
|
||||
end
|
||||
8
db/migrate/003_add_private_settings.rb
Normal file
8
db/migrate/003_add_private_settings.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
class AddPrivateSettings < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :messenger_settings, :post_private_contacts, :integer, default: 0, null: false
|
||||
add_column :messenger_settings, :post_private_db, :integer, default: 0, null: false
|
||||
end
|
||||
end
|
||||
69
init.rb
69
init.rb
@@ -1,38 +1,43 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
require 'redmine'
|
||||
require 'redmine_messenger'
|
||||
|
||||
require_dependency 'redmine_rocketchat/listener'
|
||||
Redmine::Plugin.register :redmine_messenger do
|
||||
name 'Redmine Messenger'
|
||||
author 'AlphaNodes GmbH'
|
||||
url 'https://github.com/alphanodes/redmine_messenger'
|
||||
author_url 'https://alphanodes.com/'
|
||||
description 'Messenger integration for Slack, Discord, Rocketchat and Mattermost support'
|
||||
version '1.0.2'
|
||||
|
||||
Redmine::Plugin.register :redmine_rocketchat do
|
||||
name 'Redmine Rocket.Chat'
|
||||
author 'Egon Zemmer'
|
||||
url 'https://github.com/phlegx/redmine_rocketchat'
|
||||
author_url 'https://phlegx.com'
|
||||
description 'Rocket.Chat integration'
|
||||
version '0.6.1'
|
||||
requires_redmine version_or_higher: '3.0.0'
|
||||
|
||||
requires_redmine :version_or_higher => '2.0.0'
|
||||
permission :manage_messenger, projects: :settings, messenger_settings: :save
|
||||
|
||||
settings \
|
||||
:default => {
|
||||
'callback_url' => 'https://rocket.chat/hooks/my_rocket_chat_token',
|
||||
'channel' => 'redmine',
|
||||
'icon' => 'https://raw.githubusercontent.com/phlegx/redmine_rocketchat/assets/icon.png',
|
||||
'username' => 'redmine.bot',
|
||||
'display_watchers' => '0',
|
||||
'auto_mentions' => '1',
|
||||
'post_updates' => '1',
|
||||
'new_include_description' => '1',
|
||||
'updated_include_description' => '1',
|
||||
'post_private_issues' => '1',
|
||||
'post_private_notes' => '1',
|
||||
'post_wiki_updates' => '0'
|
||||
},
|
||||
:partial => 'settings/rocketchat_settings'
|
||||
end
|
||||
|
||||
ActionDispatch::Callbacks.to_prepare do
|
||||
require_dependency 'issue'
|
||||
unless Issue.included_modules.include? RedmineRocketchat::IssuePatch
|
||||
Issue.send(:include, RedmineRocketchat::IssuePatch)
|
||||
end
|
||||
settings default: {
|
||||
messenger_url: '',
|
||||
messenger_icon: 'https://raw.githubusercontent.com/alphanodes/redmine_messenger/master/assets/images/icon.png',
|
||||
messenger_channel: 'redmine',
|
||||
messenger_username: 'robot',
|
||||
messenger_verify_ssl: '1',
|
||||
auto_mentions: '0',
|
||||
default_mentions: '',
|
||||
display_watchers: '0',
|
||||
post_updates: '1',
|
||||
new_include_description: '1',
|
||||
updated_include_description: '1',
|
||||
post_private_contacts: '0',
|
||||
post_private_db: '0',
|
||||
post_private_issues: '0',
|
||||
post_private_notes: '0',
|
||||
post_wiki: '0',
|
||||
post_wiki_updates: '0',
|
||||
post_db: '0',
|
||||
post_db_updates: '0',
|
||||
post_contact: '0',
|
||||
post_contact_updates: '0',
|
||||
post_password: '0',
|
||||
post_password_updates: '0'
|
||||
}, partial: 'settings/messenger_settings'
|
||||
end
|
||||
|
||||
34
lib/redmine_messenger.rb
Normal file
34
lib/redmine_messenger.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
Rails.configuration.to_prepare do
|
||||
module RedmineMessenger
|
||||
REDMINE_CONTACTS_SUPPORT = Redmine::Plugin.installed?('redmine_contacts') ? true : false
|
||||
REDMINE_DB_SUPPORT = Redmine::Plugin.installed?('redmine_db') ? true : false
|
||||
# this does not work at the moment, because redmine loads passwords after messener plugin
|
||||
REDMINE_PASSWORDS_SUPPORT = Redmine::Plugin.installed?('redmine_passwords') ? true : false
|
||||
|
||||
def self.settings
|
||||
ActionController::Parameters.new(Setting[:plugin_redmine_messenger])
|
||||
end
|
||||
|
||||
def self.setting?(value)
|
||||
return true if settings[value].to_i == 1
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Patches
|
||||
require_dependency 'redmine_messenger/patches/issue_patch'
|
||||
require_dependency 'redmine_messenger/patches/wiki_page_patch'
|
||||
require_dependency 'redmine_messenger/patches/projects_helper_patch'
|
||||
|
||||
require 'redmine_messenger/patches/contact_patch' if RedmineMessenger::REDMINE_CONTACTS_SUPPORT
|
||||
require 'redmine_messenger/patches/db_entry_patch' if RedmineMessenger::REDMINE_DB_SUPPORT
|
||||
require 'redmine_messenger/patches/password_patch' if Redmine::Plugin.installed?('redmine_passwords')
|
||||
|
||||
# Global helpers
|
||||
require_dependency 'redmine_messenger/helpers'
|
||||
|
||||
# Hooks
|
||||
require_dependency 'redmine_messenger/hooks'
|
||||
end
|
||||
21
lib/redmine_messenger/helpers.rb
Normal file
21
lib/redmine_messenger/helpers.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
module RedmineMessenger
|
||||
module Helpers
|
||||
def project_messenger_options(active)
|
||||
options_for_select({ l(:label_messenger_settings_default) => '0',
|
||||
l(:label_messenger_settings_disabled) => '1',
|
||||
l(:label_messenger_settings_enabled) => '2' }, active)
|
||||
end
|
||||
|
||||
def project_setting_messenger_default_value(value)
|
||||
if Messenger.default_project_setting(@project, value)
|
||||
l(:label_messenger_settings_enabled)
|
||||
else
|
||||
l(:label_messenger_settings_disabled)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActionView::Base.send :include, RedmineMessenger::Helpers
|
||||
54
lib/redmine_messenger/hooks.rb
Normal file
54
lib/redmine_messenger/hooks.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
module RedmineMessenger
|
||||
class MessengerListener < Redmine::Hook::Listener
|
||||
def model_changeset_scan_commit_for_issue_ids_pre_issue_update(context = {})
|
||||
issue = context[:issue]
|
||||
journal = issue.current_journal
|
||||
changeset = context[:changeset]
|
||||
|
||||
channels = Messenger.channels_for_project issue.project
|
||||
url = Messenger.url_for_project issue.project
|
||||
|
||||
return unless channels.present? && url && issue.changes.any? && Messenger.setting_for_project(issue.project, :post_updates)
|
||||
return if issue.is_private? && !Messenger.setting_for_project(issue.project, :post_private_issues)
|
||||
|
||||
msg = "[#{ERB::Util.html_escape(issue.project)}] #{ERB::Util.html_escape(journal.user.to_s)} updated <#{Messenger.object_url issue}|#{ERB::Util.html_escape(issue)}>"
|
||||
|
||||
repository = changeset.repository
|
||||
|
||||
if Setting.host_name.to_s =~ %r{/\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i}
|
||||
host = Regexp.last_match(2)
|
||||
port = Regexp.last_match(4)
|
||||
prefix = Regexp.last_match(5)
|
||||
revision_url = Rails.application.routes.url_for(
|
||||
controller: 'repositories',
|
||||
action: 'revision',
|
||||
id: repository.project,
|
||||
repository_id: repository.identifier_param,
|
||||
rev: changeset.revision,
|
||||
host: host,
|
||||
protocol: Setting.protocol,
|
||||
port: port,
|
||||
script_name: prefix
|
||||
)
|
||||
else
|
||||
revision_url = Rails.application.routes.url_for(
|
||||
controller: 'repositories',
|
||||
action: 'revision',
|
||||
id: repository.project,
|
||||
repository_id: repository.identifier_param,
|
||||
rev: changeset.revision,
|
||||
host: Setting.host_name,
|
||||
protocol: Setting.protocol
|
||||
)
|
||||
end
|
||||
|
||||
attachment = {}
|
||||
attachment[:text] = ll(Setting.default_language, :text_status_changed_by_changeset, "<#{revision_url}|#{ERB::Util.html_escape(changeset.comments)}>")
|
||||
attachment[:fields] = journal.details.map { |d| Messenger.detail_to_field d }
|
||||
|
||||
Messenger.speak(msg, channels, url, attachment: attachment, project: repository.project)
|
||||
end
|
||||
end
|
||||
end
|
||||
53
lib/redmine_messenger/patches/contact_patch.rb
Normal file
53
lib/redmine_messenger/patches/contact_patch.rb
Normal file
@@ -0,0 +1,53 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
module RedmineMessenger
|
||||
module Patches
|
||||
module ContactPatch
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
after_create :send_messenger_create
|
||||
after_update :send_messenger_update
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def send_messenger_create
|
||||
return unless Messenger.setting_for_project(project, :post_contact)
|
||||
return if is_private? && !Messenger.setting_for_project(project, :post_private_contacts)
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url
|
||||
Messenger.speak(l(:label_messenger_contact_created,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: "<#{Messenger.object_url self}|#{name}>",
|
||||
user: User.current),
|
||||
channels, url, project: project)
|
||||
end
|
||||
|
||||
def send_messenger_update
|
||||
return unless Messenger.setting_for_project(project, :post_contact_updates)
|
||||
return if is_private? && !Messenger.setting_for_project(project, :post_private_contacts)
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url
|
||||
Messenger.speak(l(:label_messenger_contact_updated,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: "<#{Messenger.object_url self}|#{name}>",
|
||||
user: User.current),
|
||||
channels, url, project: project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless Contact.included_modules.include? RedmineMessenger::Patches::ContactPatch
|
||||
Contact.send(:include, RedmineMessenger::Patches::ContactPatch)
|
||||
end
|
||||
54
lib/redmine_messenger/patches/db_entry_patch.rb
Normal file
54
lib/redmine_messenger/patches/db_entry_patch.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
module RedmineMessenger
|
||||
module Patches
|
||||
module DbEntryPatch
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
after_create :send_messenger_create
|
||||
after_update :send_messenger_update
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def send_messenger_create
|
||||
return unless Messenger.setting_for_project(project, :post_db)
|
||||
return if is_private? && !Messenger.setting_for_project(project, :post_private_db)
|
||||
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url
|
||||
Messenger.speak(l(:label_messenger_db_entry_created,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: "<#{Messenger.object_url self}|#{name}>",
|
||||
user: User.current),
|
||||
channels, url, project: project)
|
||||
end
|
||||
|
||||
def send_messenger_update
|
||||
return unless Messenger.setting_for_project(project, :post_db_updates)
|
||||
return if is_private? && !Messenger.setting_for_project(project, :post_private_db)
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url
|
||||
Messenger.speak(l(:label_messenger_db_entry_updated,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: "<#{Messenger.object_url self}|#{name}>",
|
||||
user: User.current),
|
||||
channels, url, project: project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless DbEntry.included_modules.include? RedmineMessenger::Patches::DbEntryPatch
|
||||
DbEntry.send(:include, RedmineMessenger::Patches::DbEntryPatch)
|
||||
end
|
||||
114
lib/redmine_messenger/patches/issue_patch.rb
Normal file
114
lib/redmine_messenger/patches/issue_patch.rb
Normal file
@@ -0,0 +1,114 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
module RedmineMessenger
|
||||
module Patches
|
||||
module IssuePatch
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
after_create :send_messenger_create
|
||||
after_update :send_messenger_update
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def send_messenger_create
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url
|
||||
return if is_private? && !Messenger.setting_for_project(project, :post_private_issues)
|
||||
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
attachment = {}
|
||||
if description.present? && Messenger.setting_for_project(project, :new_include_description)
|
||||
attachment[:text] = ERB::Util.html_escape(description)
|
||||
end
|
||||
attachment[:fields] = [{ title: I18n.t(:field_status),
|
||||
value: ERB::Util.html_escape(status.to_s),
|
||||
short: true },
|
||||
{ title: I18n.t(:field_priority),
|
||||
value: ERB::Util.html_escape(priority.to_s),
|
||||
short: true }]
|
||||
if assigned_to.present?
|
||||
attachment[:fields] << { title: I18n.t(:field_assigned_to),
|
||||
value: ERB::Util.html_escape(assigned_to.to_s),
|
||||
short: true }
|
||||
end
|
||||
|
||||
if RedmineMessenger.setting?(:display_watchers) && watcher_users.count > 0
|
||||
attachment[:fields] << {
|
||||
title: I18n.t(:field_watcher),
|
||||
value: ERB::Util.html_escape(watcher_users.join(', ')),
|
||||
short: true
|
||||
}
|
||||
end
|
||||
|
||||
Messenger.speak(l(:label_messenger_issue_created,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: send_messenger_mention_url(project, description),
|
||||
user: author),
|
||||
channels, url, attachment: attachment, project: project)
|
||||
end
|
||||
|
||||
def send_messenger_update
|
||||
return if current_journal.nil?
|
||||
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url && Messenger.setting_for_project(project, :post_updates)
|
||||
return if is_private? && !Messenger.setting_for_project(project, :post_private_issues)
|
||||
return if current_journal.private_notes? && !Messenger.setting_for_project(project, :post_private_notes)
|
||||
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
attachment = {}
|
||||
if current_journal.notes.present? && Messenger.setting_for_project(project, :updated_include_description)
|
||||
attachment[:text] = ERB::Util.html_escape(current_journal.notes)
|
||||
end
|
||||
|
||||
fields = current_journal.details.map { |d| Messenger.detail_to_field d }
|
||||
if status_id != status_id_was
|
||||
fields << { title: I18n.t(:field_status),
|
||||
value: ERB::Util.html_escape(status.to_s),
|
||||
short: true }
|
||||
end
|
||||
if priority_id != priority_id_was
|
||||
fields << { title: I18n.t(:field_priority),
|
||||
value: ERB::Util.html_escape(priority.to_s),
|
||||
short: true }
|
||||
end
|
||||
if assigned_to.present?
|
||||
fields << { title: I18n.t(:field_assigned_to),
|
||||
value: ERB::Util.html_escape(assigned_to.to_s),
|
||||
short: true }
|
||||
end
|
||||
attachment[:fields] = fields if fields.any?
|
||||
|
||||
Messenger.speak(l(:label_messenger_issue_updated,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: send_messenger_mention_url(project, current_journal.notes),
|
||||
user: current_journal.user),
|
||||
channels, url, attachment: attachment, project: project)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_messenger_mention_url(project, text)
|
||||
mention_to = ''
|
||||
if Messenger.setting_for_project(project, :auto_mentions) ||
|
||||
Messenger.textfield_for_project(project, :default_mentions).present?
|
||||
mention_to = Messenger.mentions(project, text)
|
||||
end
|
||||
"<#{Messenger.object_url(self)}|#{ERB::Util.html_escape(self)}>#{mention_to}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless WikiPage.included_modules.include? RedmineMessenger::Patches::WikiPagePatch
|
||||
WikiPage.send(:include, RedmineMessenger::Patches::WikiPagePatch)
|
||||
end
|
||||
54
lib/redmine_messenger/patches/password_patch.rb
Normal file
54
lib/redmine_messenger/patches/password_patch.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
module RedmineMessenger
|
||||
module Patches
|
||||
module PasswordPatch
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
after_create :send_messenger_create
|
||||
after_update :send_messenger_update
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def send_messenger_create
|
||||
return unless Messenger.setting_for_project(project, :post_password)
|
||||
return if is_private?
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url
|
||||
|
||||
Messenger.speak(l(:label_messenger_password_created,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: "<#{Messenger.object_url self}|#{name}>",
|
||||
user: User.current),
|
||||
channels, url, project: project)
|
||||
end
|
||||
|
||||
def send_messenger_update
|
||||
return unless Messenger.setting_for_project(project, :post_password_updates)
|
||||
return if is_private?
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url
|
||||
Messenger.speak(l(:label_messenger_password_updated,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: "<#{Messenger.object_url self}|#{name}>",
|
||||
user: User.current),
|
||||
channels, url, project: project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless Password.included_modules.include? RedmineMessenger::Patches::PasswordPatch
|
||||
Password.send(:include, RedmineMessenger::Patches::PasswordPatch)
|
||||
end
|
||||
32
lib/redmine_messenger/patches/projects_helper_patch.rb
Normal file
32
lib/redmine_messenger/patches/projects_helper_patch.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
module RedmineMessenger
|
||||
module Patches
|
||||
module ProjectsHelperPatch
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
alias_method_chain :project_settings_tabs, :messenger
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def project_settings_tabs_with_messenger
|
||||
tabs = project_settings_tabs_without_messenger
|
||||
action = { name: 'messenger',
|
||||
controller: 'messenger_settings',
|
||||
action: :show,
|
||||
partial: 'messenger_settings/show',
|
||||
label: :label_messenger }
|
||||
|
||||
tabs << action if User.current.allowed_to?(:manage_messenger, @project)
|
||||
tabs
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless ProjectsHelper.included_modules.include?(RedmineMessenger::Patches::ProjectsHelperPatch)
|
||||
ProjectsHelper.send(:include, RedmineMessenger::Patches::ProjectsHelperPatch)
|
||||
end
|
||||
56
lib/redmine_messenger/patches/wiki_page_patch.rb
Normal file
56
lib/redmine_messenger/patches/wiki_page_patch.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
# Redmine Messenger plugin for Redmine
|
||||
|
||||
module RedmineMessenger
|
||||
module Patches
|
||||
module WikiPagePatch
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
after_create :send_messenger_create
|
||||
after_update :send_messenger_update
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def send_messenger_create
|
||||
return unless Messenger.setting_for_project(project, :post_wiki)
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url
|
||||
Messenger.speak(l(:label_messenger_wiki_created,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: "<#{Messenger.object_url self}|#{title}>",
|
||||
user: User.current),
|
||||
channels, url, project: project)
|
||||
end
|
||||
|
||||
def send_messenger_update
|
||||
return unless Messenger.setting_for_project(project, :post_wiki_updates)
|
||||
set_language_if_valid Setting.default_language
|
||||
|
||||
channels = Messenger.channels_for_project project
|
||||
url = Messenger.url_for_project project
|
||||
|
||||
return unless channels.present? && url
|
||||
|
||||
attachment = nil
|
||||
unless content.comments.empty?
|
||||
attachment = {}
|
||||
attachment[:text] = ERB::Util.html_escape(content.comments.to_s)
|
||||
end
|
||||
|
||||
Messenger.speak(l(:label_messenger_wiki_updated,
|
||||
project_url: "<#{Messenger.object_url project}|#{ERB::Util.html_escape(project)}>",
|
||||
url: "<#{Messenger.object_url self}|#{title}>",
|
||||
user: content.author),
|
||||
channels, url, project: project, attachment: attachment)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Issue.send(:include, RedmineMessenger::Patches::IssuePatch) unless Issue.included_modules.include? RedmineMessenger::Patches::IssuePatch
|
||||
@@ -1,33 +0,0 @@
|
||||
module RedmineRocketchat
|
||||
module IssuePatch
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend(ClassMethods)
|
||||
base.send(:include, InstanceMethods)
|
||||
|
||||
base.class_eval do
|
||||
unloadable # Send unloadable so it will not be unloaded in development
|
||||
after_create :create_from_issue
|
||||
after_save :save_from_issue
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
def create_from_issue
|
||||
@create_already_fired = true
|
||||
Redmine::Hook.call_hook(:redmine_rocketchat_issues_new_after_save, { :issue => self})
|
||||
return true
|
||||
end
|
||||
|
||||
def save_from_issue
|
||||
unless @create_already_fired
|
||||
Redmine::Hook.call_hook(:redmine_rocketchat_issues_edit_after_save, {:issue => self, :journal => self.current_journal}) unless self.current_journal.nil?
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,320 +0,0 @@
|
||||
require 'httpclient'
|
||||
|
||||
class RocketchatListener < Redmine::Hook::Listener
|
||||
def redmine_rocketchat_issues_new_after_save(context={})
|
||||
issue = context[:issue]
|
||||
|
||||
channels = channels_for_project issue.project
|
||||
url = url_for_project issue.project
|
||||
post_private_issues = post_private_issues_for_project(issue.project)
|
||||
|
||||
return unless channels.present? and url
|
||||
return if issue.is_private? and post_private_issues != '1'
|
||||
|
||||
msg = "[#{escape issue.project}] #{escape issue.author} created <#{object_url issue}|#{escape issue}>#{mentions issue.description if Setting.plugin_redmine_rocketchat[:auto_mentions] == '1'}"
|
||||
|
||||
attachment = {}
|
||||
attachment[:text] = escape issue.description if issue.description and Setting.plugin_redmine_rocketchat[:new_include_description] == '1'
|
||||
attachment[:fields] = [{
|
||||
:title => I18n.t(:field_status),
|
||||
:value => escape(issue.status.to_s),
|
||||
:short => true
|
||||
}, {
|
||||
:title => I18n.t(:field_priority),
|
||||
:value => escape(issue.priority.to_s),
|
||||
:short => true
|
||||
}, {
|
||||
:title => I18n.t(:field_assigned_to),
|
||||
:value => escape(issue.assigned_to.to_s),
|
||||
:short => true
|
||||
}]
|
||||
|
||||
attachment[:fields] << {
|
||||
:title => I18n.t(:field_watcher),
|
||||
:value => escape(issue.watcher_users.join(', ')),
|
||||
:short => true
|
||||
} if Setting.plugin_redmine_rocketchat[:display_watchers] == '1'
|
||||
|
||||
speak msg, channels, attachment, url
|
||||
end
|
||||
|
||||
def redmine_rocketchat_issues_edit_after_save(context={})
|
||||
issue = context[:issue]
|
||||
journal = context[:journal]
|
||||
|
||||
channels = channels_for_project issue.project
|
||||
url = url_for_project issue.project
|
||||
post_private_issues = post_private_issues_for_project(issue.project)
|
||||
post_private_notes = post_private_notes_for_project(issue.project)
|
||||
|
||||
return unless channels.present? and url and Setting.plugin_redmine_rocketchat[:post_updates] == '1'
|
||||
return if issue.is_private? and post_private_issues != '1'
|
||||
return if journal.private_notes? and post_private_notes != '1'
|
||||
|
||||
msg = "[#{escape issue.project}] #{escape journal.user.to_s} updated <#{object_url issue}|#{escape issue}>#{mentions journal.notes if Setting.plugin_redmine_rocketchat[:auto_mentions] == '1'}"
|
||||
|
||||
attachment = {}
|
||||
attachment[:text] = escape journal.notes if journal.notes and Setting.plugin_redmine_rocketchat[:updated_include_description] == '1'
|
||||
attachment[:fields] = journal.details.map { |d| detail_to_field d }
|
||||
|
||||
speak msg, channels, attachment, url
|
||||
end
|
||||
|
||||
def model_changeset_scan_commit_for_issue_ids_pre_issue_update(context={})
|
||||
issue = context[:issue]
|
||||
journal = issue.current_journal
|
||||
changeset = context[:changeset]
|
||||
|
||||
channels = channels_for_project issue.project
|
||||
url = url_for_project issue.project
|
||||
post_private_issues = post_private_issues_for_project(issue.project)
|
||||
|
||||
return unless channels.present? and url and issue.save
|
||||
return if issue.is_private? and post_private_issues != '1'
|
||||
|
||||
msg = "[#{escape issue.project}] #{escape journal.user.to_s} updated <#{object_url issue}|#{escape issue}>"
|
||||
|
||||
repository = changeset.repository
|
||||
|
||||
if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
|
||||
host, port, prefix = $2, $4, $5
|
||||
revision_url = Rails.application.routes.url_for(
|
||||
:controller => 'repositories',
|
||||
:action => 'revision',
|
||||
:id => repository.project,
|
||||
:repository_id => repository.identifier_param,
|
||||
:rev => changeset.revision,
|
||||
:host => host,
|
||||
:protocol => Setting.protocol,
|
||||
:port => port,
|
||||
:script_name => prefix
|
||||
)
|
||||
else
|
||||
revision_url = Rails.application.routes.url_for(
|
||||
:controller => 'repositories',
|
||||
:action => 'revision',
|
||||
:id => repository.project,
|
||||
:repository_id => repository.identifier_param,
|
||||
:rev => changeset.revision,
|
||||
:host => Setting.host_name,
|
||||
:protocol => Setting.protocol
|
||||
)
|
||||
end
|
||||
|
||||
attachment = {}
|
||||
attachment[:text] = ll(Setting.default_language, :text_status_changed_by_changeset, "<#{revision_url}|#{escape changeset.comments}>")
|
||||
attachment[:fields] = journal.details.map { |d| detail_to_field d }
|
||||
|
||||
speak msg, channels, attachment, url
|
||||
end
|
||||
|
||||
def controller_wiki_edit_after_save(context = { })
|
||||
return unless Setting.plugin_redmine_rocketchat[:post_wiki_updates] == '1'
|
||||
|
||||
project = context[:project]
|
||||
page = context[:page]
|
||||
|
||||
user = page.content.author
|
||||
project_url = "<#{object_url project}|#{escape project}>"
|
||||
page_url = "<#{object_url page}|#{page.title}>"
|
||||
comment = "[#{project_url}] #{page_url} updated by *#{user}*"
|
||||
|
||||
channels = channels_for_project project
|
||||
url = url_for_project project
|
||||
|
||||
return unless channels.present? and url
|
||||
|
||||
attachment = nil
|
||||
unless page.content.comments.empty?
|
||||
attachment = {}
|
||||
attachment[:text] = "#{escape page.content.comments}"
|
||||
end
|
||||
|
||||
speak comment, channels, attachment, url
|
||||
end
|
||||
|
||||
def speak(msg, channels, attachment=nil, url=nil)
|
||||
return if channels.blank?
|
||||
|
||||
url = Setting.plugin_redmine_rocketchat[:rocketchat_url] unless url
|
||||
username = Setting.plugin_redmine_rocketchat[:username]
|
||||
icon = Setting.plugin_redmine_rocketchat[:icon]
|
||||
|
||||
params = {
|
||||
:text => msg,
|
||||
:link_names => 1,
|
||||
}
|
||||
|
||||
params[:username] = username if username
|
||||
|
||||
|
||||
params[:attachments] = [attachment] if attachment
|
||||
|
||||
if icon and not icon.empty?
|
||||
if icon.start_with? ':'
|
||||
params[:icon_emoji] = icon
|
||||
else
|
||||
params[:icon_url] = icon
|
||||
end
|
||||
end
|
||||
|
||||
channels.each do |channel|
|
||||
params[:channel] = channel
|
||||
|
||||
begin
|
||||
client = HTTPClient.new
|
||||
client.ssl_config.cert_store.set_default_paths
|
||||
client.ssl_config.ssl_version = :auto
|
||||
client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
||||
client.post_async url, {:payload => params.to_json}
|
||||
rescue Exception => e
|
||||
Rails.logger.warn("cannot connect to #{url}")
|
||||
Rails.logger.warn(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def escape(msg)
|
||||
msg.to_s.gsub('&', '&').gsub('<', '<').gsub('>', '>')
|
||||
end
|
||||
|
||||
def object_url(obj)
|
||||
if Setting.host_name.to_s =~ /\A(https?\:\/\/)?(.+?)(\:(\d+))?(\/.+)?\z/i
|
||||
host, port, prefix = $2, $4, $5
|
||||
Rails.application.routes.url_for(obj.event_url({:host => host, :protocol => Setting.protocol, :port => port, :script_name => prefix}))
|
||||
else
|
||||
Rails.application.routes.url_for(obj.event_url({:host => Setting.host_name, :protocol => Setting.protocol}))
|
||||
end
|
||||
end
|
||||
|
||||
def url_for_project(proj)
|
||||
return nil if proj.blank?
|
||||
|
||||
cf = ProjectCustomField.find_by_name('Rocket.Chat URL')
|
||||
|
||||
[
|
||||
(proj.custom_value_for(cf).value rescue nil),
|
||||
(url_for_project proj.parent),
|
||||
Setting.plugin_redmine_rocketchat[:rocketchat_url],
|
||||
].flatten.find(&:present?)
|
||||
end
|
||||
|
||||
def post_private_issues_for_project(proj)
|
||||
return nil if proj.blank?
|
||||
|
||||
cf = ProjectCustomField.find_by_name('Rocket.Chat Post private issues')
|
||||
[
|
||||
(proj.custom_value_for(cf).value rescue nil),
|
||||
(post_private_issues_for_project proj.parent),
|
||||
Setting.plugin_redmine_rocketchat[:post_private_issues],
|
||||
].flatten.find(&:present?)
|
||||
end
|
||||
|
||||
def post_private_notes_for_project(proj)
|
||||
return nil if proj.blank?
|
||||
|
||||
cf = ProjectCustomField.find_by_name('Rocket.Chat Post private notes')
|
||||
[
|
||||
(proj.custom_value_for(cf).value rescue nil),
|
||||
(post_private_notes_for_project proj.parent),
|
||||
Setting.plugin_redmine_rocketchat[:post_private_notes],
|
||||
].flatten.find(&:present?)
|
||||
end
|
||||
|
||||
def channels_for_project(proj)
|
||||
return nil if proj.blank?
|
||||
|
||||
cf = ProjectCustomField.find_by_name('Rocket.Chat Channel')
|
||||
val = [
|
||||
(proj.custom_value_for(cf).value rescue nil),
|
||||
(channels_for_project proj.parent),
|
||||
Setting.plugin_redmine_rocketchat[:channel],
|
||||
].flatten.find(&:present?)
|
||||
|
||||
# Channel name '-' or empty '' is reserved for NOT notifying
|
||||
return [] if val.nil? or val.to_s == ''
|
||||
return [] if val.to_s == '-'
|
||||
return val.split(",") if val.is_a? String
|
||||
val
|
||||
end
|
||||
|
||||
def detail_to_field(detail)
|
||||
field_format = nil
|
||||
|
||||
if detail.property == "cf"
|
||||
key = CustomField.find(detail.prop_key).name rescue nil
|
||||
title = key
|
||||
field_format = CustomField.find(detail.prop_key).field_format rescue nil
|
||||
elsif detail.property == 'attachment'
|
||||
key = 'attachment'
|
||||
title = I18n.t :label_attachment
|
||||
else
|
||||
key = detail.prop_key.to_s.sub('_id', '')
|
||||
title = I18n.t "field_#{key}"
|
||||
end
|
||||
|
||||
short = true
|
||||
value = escape detail.value.to_s
|
||||
|
||||
case key
|
||||
when 'title', 'subject', 'description'
|
||||
short = false
|
||||
when 'tracker'
|
||||
tracker = Tracker.find(detail.value) rescue nil
|
||||
value = escape tracker.to_s
|
||||
when 'project'
|
||||
project = Project.find(detail.value) rescue nil
|
||||
value = escape project.to_s
|
||||
when 'status'
|
||||
status = IssueStatus.find(detail.value) rescue nil
|
||||
value = escape status.to_s
|
||||
when 'priority'
|
||||
priority = IssuePriority.find(detail.value) rescue nil
|
||||
value = escape priority.to_s
|
||||
when 'category'
|
||||
category = IssueCategory.find(detail.value) rescue nil
|
||||
value = escape category.to_s
|
||||
when 'assigned_to'
|
||||
user = User.find(detail.value) rescue nil
|
||||
value = escape user.to_s
|
||||
when 'fixed_version'
|
||||
version = Version.find(detail.value) rescue nil
|
||||
value = escape version.to_s
|
||||
when 'attachment'
|
||||
attachment = Attachment.find(detail.prop_key) rescue nil
|
||||
value = "<#{object_url attachment}|#{escape attachment.filename}>" if attachment
|
||||
when 'parent'
|
||||
issue = Issue.find(detail.value) rescue nil
|
||||
value = "<#{object_url issue}|#{escape issue}>" if issue
|
||||
end
|
||||
|
||||
case field_format
|
||||
when 'version'
|
||||
version = Version.find(detail.value) rescue nil
|
||||
value = escape version.to_s
|
||||
end
|
||||
|
||||
value = '-' if value.empty?
|
||||
|
||||
result = { :title => title, :value => value }
|
||||
result[:short] = true if short
|
||||
result
|
||||
end
|
||||
|
||||
def mentions text
|
||||
return nil if text.nil?
|
||||
names = extract_usernames text
|
||||
names.present? ? '\nTo: ' + names.join(', ') : nil
|
||||
end
|
||||
|
||||
def extract_usernames text = ''
|
||||
if text.nil?
|
||||
text = ''
|
||||
end
|
||||
|
||||
# rocketchat usernames may only contain lowercase letters, numbers,
|
||||
# dashes, dots and underscores and must start with a letter or number.
|
||||
text.scan(/@[a-z0-9][a-z0-9_\-.]*/).uniq
|
||||
end
|
||||
end
|
||||
41
test/integration/common_views_test.rb
Normal file
41
test/integration/common_views_test.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
require File.expand_path('../../test_helper', __FILE__)
|
||||
|
||||
class CommonViewsTest < Redmine::IntegrationTest
|
||||
fixtures :projects,
|
||||
:users,
|
||||
:roles,
|
||||
:members,
|
||||
:member_roles,
|
||||
:trackers,
|
||||
:projects_trackers,
|
||||
:enabled_modules,
|
||||
:issue_statuses,
|
||||
:issues,
|
||||
:enumerations,
|
||||
:custom_fields,
|
||||
:custom_values,
|
||||
:custom_fields_trackers
|
||||
|
||||
def setup
|
||||
RedmineMessenger::TestCase.prepare
|
||||
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
@request.env['HTTP_REFERER'] = '/'
|
||||
end
|
||||
|
||||
test 'View user' do
|
||||
log_user('admin', 'admin')
|
||||
get '/users/2'
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test 'View issue' do
|
||||
log_user('admin', 'admin')
|
||||
EnabledModule.create(project_id: 1, name: 'issue_tracking')
|
||||
issue = Issue.where(id: 1).first
|
||||
issue.save
|
||||
get '/issues/1'
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
56
test/test_helper.rb
Normal file
56
test/test_helper.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
unless ENV['SKIP_COVERAGE']
|
||||
require 'simplecov'
|
||||
require 'simplecov-rcov'
|
||||
|
||||
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
||||
SimpleCov::Formatter::HTMLFormatter,
|
||||
SimpleCov::Formatter::RcovFormatter
|
||||
]
|
||||
|
||||
SimpleCov.start :rails do
|
||||
add_filter 'init.rb'
|
||||
root File.expand_path(File.dirname(__FILE__) + '/..')
|
||||
end
|
||||
end
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')
|
||||
|
||||
module RedmineMessenger
|
||||
class TestCase
|
||||
include ActionDispatch::TestProcess
|
||||
def self.plugin_fixtures(plugin, *fixture_names)
|
||||
plugin_fixture_path = "#{Redmine::Plugin.find(plugin).directory}/test/fixtures"
|
||||
if fixture_names.first == :all
|
||||
fixture_names = Dir["#{plugin_fixture_path}/**/*.{yml}"]
|
||||
fixture_names.map! { |f| f[(plugin_fixture_path.size + 1)..-5] }
|
||||
else
|
||||
fixture_names = fixture_names.flatten.map(&:to_s)
|
||||
end
|
||||
|
||||
ActiveRecord::Fixtures.create_fixtures(plugin_fixture_path, fixture_names)
|
||||
end
|
||||
|
||||
def uploaded_test_file(name, mime)
|
||||
ActionController::TestUploadedFile.new(ActiveSupport::TestCase.fixture_path + "/files/#{name}", mime, true)
|
||||
end
|
||||
|
||||
def self.arrays_equal?(a1, a2)
|
||||
(a1 - a2) - (a2 - a1) == []
|
||||
end
|
||||
|
||||
def self.create_fixtures(fixtures_directory, table_names, _class_names = {})
|
||||
ActiveRecord::FixtureSet.create_fixtures(fixtures_directory, table_names, _class_names = {})
|
||||
end
|
||||
|
||||
def self.prepare
|
||||
Role.where(id: [1, 2]).each do |r|
|
||||
r.permissions << :view_issues
|
||||
r.save
|
||||
end
|
||||
|
||||
Project.where(id: [1, 2]).each do |project|
|
||||
EnabledModule.create(project: project, name: 'issue_tracking')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
40
test/unit/i18n_test.rb
Normal file
40
test/unit/i18n_test.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
require File.expand_path('../../test_helper', __FILE__)
|
||||
|
||||
module Redmine
|
||||
class I18nTest < ActiveSupport::TestCase
|
||||
include Redmine::I18n
|
||||
|
||||
def setup
|
||||
User.current = nil
|
||||
end
|
||||
|
||||
def teardown
|
||||
set_language_if_valid 'en'
|
||||
end
|
||||
|
||||
def test_valid_languages
|
||||
assert valid_languages.is_a?(Array)
|
||||
assert valid_languages.first.is_a?(Symbol)
|
||||
end
|
||||
|
||||
def test_locales_validness
|
||||
lang_files_count = Dir[Rails.root.join('plugins',
|
||||
'redmine_messenger',
|
||||
'config',
|
||||
'locales',
|
||||
'*.yml')].size
|
||||
assert_equal lang_files_count, 3
|
||||
valid_languages.each do |lang|
|
||||
assert set_language_if_valid(lang)
|
||||
end
|
||||
# check if parse error exists
|
||||
::I18n.locale = 'de'
|
||||
assert_equal 'Messenger Benutzer', l(:label_settings_messenger_username)
|
||||
::I18n.locale = 'en'
|
||||
assert_equal 'Messenger username', l(:label_settings_messenger_username)
|
||||
::I18n.locale = 'ja'
|
||||
assert_equal 'メッセンジャーのユーザー名', l(:label_settings_messenger_username)
|
||||
set_language_if_valid('en')
|
||||
end
|
||||
end
|
||||
end
|
||||
29
test/unit/issue_test.rb
Normal file
29
test/unit/issue_test.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
require File.expand_path('../../test_helper', __FILE__)
|
||||
|
||||
class IssueTest < ActiveSupport::TestCase
|
||||
fixtures :projects, :users, :members, :member_roles, :roles,
|
||||
:trackers, :projects_trackers,
|
||||
:enabled_modules,
|
||||
:issue_statuses, :issue_categories, :workflows,
|
||||
:enumerations,
|
||||
:issues, :journals, :journal_details,
|
||||
:custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
|
||||
:time_entries
|
||||
|
||||
include Redmine::I18n
|
||||
|
||||
def setup
|
||||
set_language_if_valid 'en'
|
||||
end
|
||||
|
||||
def teardown
|
||||
User.current = nil
|
||||
end
|
||||
|
||||
def test_create
|
||||
issue = Issue.new(project_id: 1, tracker_id: 1, author_id: 3, subject: 'test_create')
|
||||
assert issue.save
|
||||
assert_equal issue.tracker.default_status, issue.status
|
||||
assert issue.description.nil?
|
||||
end
|
||||
end
|
||||
37
test/unit/project_test.rb
Normal file
37
test/unit/project_test.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
require File.expand_path('../../test_helper', __FILE__)
|
||||
|
||||
class ProjectTest < ActiveSupport::TestCase
|
||||
fixtures :projects, :trackers, :issue_statuses, :issues,
|
||||
:journals, :journal_details,
|
||||
:enumerations, :users, :issue_categories,
|
||||
:projects_trackers,
|
||||
:custom_fields,
|
||||
:custom_fields_projects,
|
||||
:custom_fields_trackers,
|
||||
:custom_values,
|
||||
:roles,
|
||||
:member_roles,
|
||||
:members,
|
||||
:enabled_modules,
|
||||
:versions,
|
||||
:wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
|
||||
:groups_users,
|
||||
:time_entries,
|
||||
:news, :comments,
|
||||
:documents,
|
||||
:workflows
|
||||
|
||||
def setup
|
||||
User.current = User.find(1)
|
||||
end
|
||||
|
||||
def test_create_project
|
||||
Project.delete_all
|
||||
Project.create!(name: 'Project Messenger', identifier: 'project-messenger')
|
||||
assert_equal 1, Project.count
|
||||
end
|
||||
|
||||
def test_load_project
|
||||
Project.find(1)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user