├── mantis_logo.png
├── config
├── mysql
│ └── mantis.cnf
└── config_inc.php
├── docker-compose.yml
├── Dockerfile
├── README.md
├── lang
├── strings_german.txt
├── strings_russian.txt
├── strings_french.txt
└── strings_english.txt
├── pages
├── config.php
└── config_page.php
├── LICENSE
└── Slack.php
/mantis_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mantisbt-plugins/Slack/HEAD/mantis_logo.png
--------------------------------------------------------------------------------
/config/mysql/mantis.cnf:
--------------------------------------------------------------------------------
1 | [mysqld]
2 | max_allowed_packet=32M
3 | character-set-server = utf8
4 | collation-server = utf8_unicode_ci
5 |
--------------------------------------------------------------------------------
/config/config_inc.php:
--------------------------------------------------------------------------------
1 | > /usr/local/etc/php/conf.d/mantis.ini \
27 | && echo 'log_errors = 1' >> /usr/local/etc/php/conf.d/mantis.ini \
28 | && echo 'upload_max_filesize = 20M' >> /usr/local/etc/php/conf.d/mantis.ini \
29 | && echo 'post_max_size = 21M' >> /usr/local/etc/php/conf.d/mantis.ini
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | MantisBT-Slack
2 | ==============
3 |
4 | A [MantisBT](http://www.mantisbt.org/) plugin to send bug updates to [Slack](https://slack.com/), [Mattermost](https://about.mattermost.com/) and [Discord](https://discord.com/) channels.
5 |
6 | # Setup
7 | * The `master` branch requires Mantis 2.x, while the `master-1.2.x` branch works for Mantis 1.2.x.
8 | * Extract this repo to your *Mantis folder/plugins/Slack*.
9 | * On the Slack side, add a new "Incoming Webhooks" integration and note the URL that Slack generates for you.
10 | * On the MantisBT side, access the plugin's configuration page and fill in your Slack webhook URL.
11 | * You can map your MantisBT projects to Slack channels by setting the *plugin_Slack_channels* option in Mantis. Follow the instructions on the plugin's configuration page to get there. Make sure the *plugin_Slack_channels* configuration option is set to "All Users", with type "complex".
12 | Example value for this setting:
13 |
14 | array (
15 | 'My First Mantis Project' => '#general',
16 | 'My Second Mantis Project' => '#second-project'
17 | )
18 |
19 | * You can specify which bug fields appear in the Slack notifications. Edit the *plugin_Slack_columns* configuration option for this purpose. Follow the
20 | instructions on the plugin configuration page.
21 |
22 | * For Discord, you need to append `/slack` so that Discord handles this as a Slack-compatible webhook. [More info here](https://discord.com/developers/docs/resources/webhook#execute-slackcompatible-webhook).
23 |
24 | # Development
25 | You can run a local development environment using Docker Compose:
26 | - `docker-compose build && docker-compose up`
27 | - Open http://localhost:8080/admin/install.php and install the database (using Admin credentials `root` / `root`)
28 | - Login to Mantis using `administrator` / `root`
29 | - Enable Slack plugin at http://localhost:8080/manage_plugin_page.php
30 |
--------------------------------------------------------------------------------
/lang/strings_german.txt:
--------------------------------------------------------------------------------
1 |
21 | */
22 |
23 | $s_plugin_Slack_ = '';
24 | $s_plugin_Slack_title = 'Slack Integration';
25 | $s_plugin_Slack_description = 'Fügt die Integration von Slack (https://slack.com) zu Mantis hinzu.';
26 | $s_plugin_Slack_config = 'Konfiguration';
27 | $s_plugin_Slack_url_webhook = 'Default Slack Webhook URL';
28 | $s_plugin_Slack_url_webhooks = 'Additional Slack Webhook URLs';
29 | $s_plugin_Slack_bot_name = 'Slack Bot Name';
30 | $s_plugin_Slack_bot_icon = 'Slack Bot Icon';
31 | $s_plugin_Slack_default_channel = 'Standard Slack Channel';
32 | $s_plugin_Slack_channels = 'Slack Channels';
33 | $s_plugin_Slack_columns = 'Slack Spalten';
34 | $s_plugin_Slack_bug_created = '[%s] %s erstellte <%s|%s>.';
35 | $s_plugin_Slack_bug_updated = '[%s] %s aktualisierte <%s|%s>.';
36 | $s_plugin_Slack_bug_deleted = '[%s] %s löschte %s.';
37 | $s_plugin_Slack_bugnote_created = "[%s] %s kommentierte den Eintrag <%s|%s>:";
38 | $s_plugin_Slack_bugnote_updated = "[%s] %s bearbeitete einen Kommentar im Eintrag <%s|%s>:";
39 | $s_plugin_Slack_bugnote_deleted = "[%s] %s löschte einen Kommentar im Eintrag <%s|%s>.";
40 | $s_plugin_Slack_no_user = '(niemand)';
41 | $s_plugin_Slack_unknown_field = '(Es liegt keine Information vor, wie das Feld "%s" ausgegeben werden kann.)';
42 |
43 | $MANTIS_ERROR['plugin_Slack_ERROR_NO_CURL'] = 'Das Slack Plugin benötigt die cURL PHP Erweiterung (http://php.net/curl)';
44 | $MANTIS_ERROR['plugin_Slack_ERROR_PHP_VERSION'] = 'Das Slack plugin benötigt PHP 5.3.0 oder höher';
45 |
--------------------------------------------------------------------------------
/lang/strings_russian.txt:
--------------------------------------------------------------------------------
1 | .';
37 | $s_plugin_Slack_bug_updated = '[%s] %s изменил <%s|%s>.';
38 | $s_plugin_Slack_bug_deleted = '[%s] %s удалил %s.';
39 | $s_plugin_Slack_bugnote_created = "[%s] %s прокомментировал <%s|%s>:";
40 | $s_plugin_Slack_bugnote_updated = "[%s] %s изменил комментарий к <%s|%s>:";
41 | $s_plugin_Slack_bugnote_deleted = "[%s] %s удалил комментарий к <%s|%s>.";
42 | $s_plugin_Slack_no_user = '(никто)';
43 | $s_plugin_Slack_unknown_field = '(не знаю, как выводить поле "%s")';
44 | $s_plugin_Slack_skip = 'Пропустить оповещение в Slack';
45 | $s_plugin_Slack_action_update = 'Обновить';
46 |
47 | $MANTIS_ERROR['plugin_Slack_ERROR_NO_CURL'] = 'Slack-плагин требует PHP-расширение cURL (http://php.net/curl)';
48 | $MANTIS_ERROR['plugin_Slack_ERROR_PHP_VERSION'] = 'Slack-плагин требует PHP 5.3.0 или больше';
49 | $MANTIS_ERROR['plugin_Slack_ERROR_CURL'] = 'Не прошёл запрос к Slack. Проверьте свой webhook URL. Также убедитесь, что настройки имени и иконки бота не содержат необычных символов. Больше деталей может быть ниже.';
50 |
--------------------------------------------------------------------------------
/pages/config.php:
--------------------------------------------------------------------------------
1 | notify(
43 | plugin_lang_get('url_webhook_test_text'),
44 | gpc_get_string( 'url_webhook' ),
45 | gpc_get_string( 'default_channel' )
46 | );
47 |
48 | }
49 |
50 | config_set_if_needed( 'url_webhook' , gpc_get_string( 'url_webhook' ) );
51 | config_set_if_needed( 'bot_name' , gpc_get_string( 'bot_name' ) );
52 | config_set_if_needed( 'bot_icon' , gpc_get_string( 'bot_icon' ) );
53 | config_set_if_needed( 'skip_private' , gpc_get_bool( 'skip_private' ) );
54 | config_set_if_needed( 'skip_bulk' , gpc_get_bool( 'skip_bulk' ) );
55 | config_set_if_needed( 'link_names' , gpc_get_bool( 'link_names' ) );
56 | config_set_if_needed( 'default_channel' , gpc_get_string( 'default_channel' ) );
57 | config_set_if_needed( 'notification_bug_report' , gpc_get_bool( 'notification_bug_report' ) );
58 | config_set_if_needed( 'notification_bug_update' , gpc_get_bool( 'notification_bug_update' ) );
59 | config_set_if_needed( 'notification_bug_deleted' , gpc_get_bool( 'notification_bug_deleted' ) );
60 | config_set_if_needed( 'notification_bugnote_add' , gpc_get_bool( 'notification_bugnote_add' ) );
61 | config_set_if_needed( 'notification_bugnote_edit' , gpc_get_bool( 'notification_bugnote_edit' ) );
62 | config_set_if_needed( 'notification_bugnote_deleted' , gpc_get_bool( 'notification_bugnote_deleted' ) );
63 |
64 | form_security_purge( 'plugin_Slack_config' );
65 |
66 | html_operation_successful( $t_redirect_url );
67 | layout_page_end();
68 |
--------------------------------------------------------------------------------
/lang/strings_french.txt:
--------------------------------------------------------------------------------
1 | .';
41 | $s_plugin_Slack_bug_updated = '[%s] %s a modifié <%s|%s>.';
42 | $s_plugin_Slack_bug_deleted = '[%s] %s a effacé %s.';
43 | $s_plugin_Slack_bugnote_created = "[%s] %s a commenté sur <%s|%s> pour dire:";
44 | $s_plugin_Slack_bugnote_updated = "[%s] %s a modifié un commentaire sur <%s|%s> pour dire:";
45 | $s_plugin_Slack_bugnote_deleted = "[%s] %s a effacé un commentaire sur <%s|%s>.";
46 | $s_plugin_Slack_no_user = '(personne)';
47 | $s_plugin_Slack_unknown_field = '(incapable de visualiser le champ "%s")';
48 | $s_plugin_Slack_skip = 'Ne pas envoyer de notification sur Slack';
49 | $s_plugin_Slack_action_update = 'Envoyer';
50 |
51 | $MANTIS_ERROR['plugin_Slack_ERROR_NO_CURL'] = 'Le plugiciel Slack requiert l\'extension cURL (http://php.net/curl)';
52 | $MANTIS_ERROR['plugin_Slack_ERROR_PHP_VERSION'] = 'Le plugiciel Slack requiert PHP 5.3.0 ou plus';
53 | $MANTIS_ERROR['plugin_Slack_ERROR_CURL'] = 'La requête pour Slack n\'est pas arrivée à destination. Vérifiez votre URL de webhook. Vérifiez aussi que le nom et l\'icône du bot Slack ne contiennent pas de caractère anormaux. Regardez ci-dessous pour plus d\'informations.';
54 |
--------------------------------------------------------------------------------
/lang/strings_english.txt:
--------------------------------------------------------------------------------
1 | .';
46 | $s_plugin_Slack_bug_updated = '[%s] %s updated <%s|%s>.';
47 | $s_plugin_Slack_bug_deleted = '[%s] %s deleted %s.';
48 | $s_plugin_Slack_bugnote_created = "[%s] %s commented on <%s|%s> saying:";
49 | $s_plugin_Slack_bugnote_updated = "[%s] %s edited a comment on <%s|%s> saying:";
50 | $s_plugin_Slack_bugnote_deleted = "[%s] %s deleted a comment on <%s|%s>.";
51 | $s_plugin_Slack_no_user = '(no one)';
52 | $s_plugin_Slack_unknown_field = '(don\'t know how to render field "%s")';
53 | $s_plugin_Slack_skip = 'Skip Slack notification';
54 | $s_plugin_Slack_action_update = 'Update';
55 |
56 | $MANTIS_ERROR['plugin_Slack_ERROR_NO_CURL'] = 'The Slack plugin requires the cURL PHP extension (http://php.net/curl)';
57 | $MANTIS_ERROR['plugin_Slack_ERROR_PHP_VERSION'] = 'The Slack plugin requires PHP 5.3.0 or higher';
58 | $MANTIS_ERROR['plugin_Slack_ERROR_CURL'] = 'The request to Slack didn\'t go through. Check your webhook URL. Also make sure there are no unusual characters in your bot name or bot icon settings. Further details may be printed below this box.';
59 |
--------------------------------------------------------------------------------
/pages/config_page.php:
--------------------------------------------------------------------------------
1 |
30 |
31 |
197 |
198 | name = plugin_lang_get( 'title' );
26 | $this->description = plugin_lang_get( 'description' );
27 | $this->page = 'config_page';
28 | $this->version = '1.0.3';
29 | $this->requires = array(
30 | 'MantisCore' => '2.0.0',
31 | );
32 | $this->author = 'Karim Ratib';
33 | $this->contact = 'karim.ratib@gmail.com';
34 | $this->url = 'https://karimratib.me';
35 | }
36 |
37 | function install() {
38 | if (version_compare(PHP_VERSION, '5.3.0', '<')) {
39 | plugin_error('ERROR_PHP_VERSION');
40 | return false;
41 | }
42 | if (!extension_loaded('curl')) {
43 | plugin_error('ERROR_NO_CURL');
44 | return false;
45 | }
46 | return true;
47 | }
48 |
49 | function config() {
50 | return array(
51 | 'url_webhooks' => array(),
52 | 'url_webhook' => '',
53 | 'bot_name' => 'mantis',
54 | 'bot_icon' => '',
55 | 'skip_private' => true,
56 | 'skip_bulk' => true,
57 | 'link_names' => false,
58 | 'channels' => array(),
59 | 'default_channel' => '#general',
60 | 'usernames' => array(),
61 | 'columns' => array(
62 | 'status',
63 | 'handler_id',
64 | 'target_version',
65 | 'priority',
66 | 'severity',
67 | ),
68 | 'notification_bug_report' => true,
69 | 'notification_bug_update' => true,
70 | 'notification_bug_deleted' => true,
71 | 'notification_bugnote_add' => true,
72 | 'notification_bugnote_edit' => true,
73 | 'notification_bugnote_deleted' => true,
74 | );
75 | }
76 |
77 | function hooks() {
78 | return array(
79 | 'EVENT_REPORT_BUG' => 'bug_report',
80 | 'EVENT_UPDATE_BUG' => 'bug_update',
81 | 'EVENT_BUG_DELETED' => 'bug_deleted',
82 | 'EVENT_BUG_ACTION' => 'bug_action',
83 | 'EVENT_BUGNOTE_ADD' => 'bugnote_add_edit',
84 | 'EVENT_BUGNOTE_EDIT' => 'bugnote_add_edit',
85 | 'EVENT_BUGNOTE_DELETED' => 'bugnote_deleted',
86 | 'EVENT_BUGNOTE_ADD_FORM' => 'bugnote_add_form',
87 | );
88 | }
89 |
90 | function skip_private($bug_or_note) {
91 | return (
92 | $bug_or_note->view_state == VS_PRIVATE &&
93 | plugin_config_get('skip_private')
94 | );
95 | }
96 |
97 | function skip_event($event) {
98 | $configs = array(
99 | 'EVENT_REPORT_BUG' => 'notification_bug_report',
100 | 'EVENT_UPDATE_BUG' => 'notification_bug_update',
101 | 'EVENT_BUG_DELETED' => 'notification_bug_deleted',
102 | 'EVENT_BUGNOTE_ADD' => 'notification_bugnote_add',
103 | 'EVENT_BUGNOTE_EDIT' => 'notification_bugnote_edit',
104 | 'EVENT_BUGNOTE_DELETED' => 'notification_bugnote_deleted',
105 | );
106 | if (!array_key_exists($event, $configs)) return true;
107 | return !plugin_config_get($configs[$event]);
108 | }
109 |
110 | function bugnote_add_form($event, $bug_id) {
111 | if ($_SERVER['PHP_SELF'] !== '/bug_update_page.php') return;
112 |
113 | echo '';
114 | echo '| ' . plugin_lang_get( 'skip' ) . ' | ';
115 | echo '';
116 | echo '';
120 | echo ' |
';
121 | }
122 |
123 | function bug_report_update($event, $bug, $bug_id) {
124 | $this->skip = $this->skip ||
125 | gpc_get_bool('slack_skip') ||
126 | $this->skip_private($bug) ||
127 | $this->skip_event($event);
128 |
129 | $project = project_get_name($bug->project_id);
130 | $url = string_get_bug_view_url_with_fqdn($bug_id);
131 | $summary = $this->format_summary($bug);
132 | $reporter = $this->get_user_name(auth_get_current_user_id());
133 | $msg = sprintf(plugin_lang_get($event === 'EVENT_REPORT_BUG' ? 'bug_created' : 'bug_updated'),
134 | $project, $reporter, $url, $summary
135 | );
136 | $this->notify($msg, $this->get_webhook($project), $this->get_channel($project), $this->get_attachment($bug));
137 | }
138 |
139 | function bug_report($event, $bug, $bug_id) {
140 | $this->bug_report_update($event, $bug, $bug_id);
141 | }
142 |
143 | function bug_update($event, $bug_existing, $bug_updated) {
144 | $this->bug_report_update($event, $bug_updated, $bug_updated->id);
145 | }
146 |
147 | function bug_action($event, $action, $bug_id) {
148 | $this->skip = $this->skip ||
149 | gpc_get_bool('slack_skip') ||
150 | plugin_config_get('skip_bulk');
151 |
152 | if ($action !== 'DELETE') {
153 | $bug = bug_get($bug_id);
154 | $this->bug_report_update('EVENT_UPDATE_BUG', $bug, $bug_id);
155 | }
156 | }
157 |
158 | function bug_deleted($event, $bug_id) {
159 | $bug = bug_get($bug_id);
160 |
161 | $this->skip = $this->skip ||
162 | gpc_get_bool('slack_skip') ||
163 | $this->skip_private($bug) ||
164 | $this->skip_event($event);
165 |
166 | $project = project_get_name($bug->project_id);
167 | $reporter = $this->get_user_name(auth_get_current_user_id());
168 | $summary = $this->format_summary($bug);
169 | $msg = sprintf(plugin_lang_get('bug_deleted'), $project, $reporter, $summary);
170 | $this->notify($msg, $this->get_webhook($project), $this->get_channel($project));
171 | }
172 |
173 | function bugnote_add_edit($event, $bug_id, $bugnote_id, $files = null) {
174 | $bug = bug_get($bug_id);
175 | $bugnote = bugnote_get($bugnote_id);
176 |
177 | $this->skip = $this->skip ||
178 | gpc_get_bool('slack_skip') ||
179 | $this->skip_private($bug) ||
180 | $this->skip_private($bugnote) ||
181 | $this->skip_event($event);
182 |
183 | $url = string_get_bugnote_view_url_with_fqdn($bug_id, $bugnote_id);
184 | $project = project_get_name($bug->project_id);
185 | $summary = $this->format_summary($bug);
186 | $reporter = $this->get_user_name(auth_get_current_user_id());
187 | $note = bugnote_get_text($bugnote_id);
188 | $msg = sprintf(plugin_lang_get($event === 'EVENT_BUGNOTE_ADD' ? 'bugnote_created' : 'bugnote_updated'),
189 | $project, $reporter, $url, $summary
190 | );
191 | $this->notify($msg, $this->get_webhook($project), $this->get_channel($project), $this->get_text_attachment($this->bbcode_to_slack($note)));
192 | }
193 |
194 | function get_text_attachment($text) {
195 | $attachment = array('color' => '#3AA3E3', 'mrkdwn_in' => array('pretext', 'text', 'fields'));
196 | $attachment['fallback'] = $text . "\n";
197 | $attachment['text'] = $text;
198 | return $attachment;
199 | }
200 |
201 | function bugnote_deleted($event, $bug_id, $bugnote_id) {
202 | $bug = bug_get($bug_id);
203 | $bugnote = bugnote_get($bugnote_id);
204 |
205 | $this->skip = $this->skip ||
206 | gpc_get_bool('slack_skip') ||
207 | $this->skip_private($bug) ||
208 | $this->skip_private($bugnote) ||
209 | $this->skip_event($event);
210 |
211 | $project = project_get_name($bug->project_id);
212 | $url = string_get_bug_view_url_with_fqdn($bug_id);
213 | $summary = $this->format_summary($bug);
214 | $reporter = $this->get_user_name(auth_get_current_user_id());
215 | $msg = sprintf(plugin_lang_get('bugnote_deleted'), $project, $reporter, $url, $summary);
216 | $this->notify($msg, $this->get_webhook($project), $this->get_channel($project));
217 | }
218 |
219 | function format_summary($bug) {
220 | return bug_format_id($bug->id) . ': ' . $this->format_text($bug->summary);
221 | }
222 |
223 | function format_text($text) {
224 | return strip_tags(
225 | str_replace(
226 | array('&', '<', '>'),
227 | array('&', '<', '>'),
228 | $this->bbcode_to_slack($text)
229 | )
230 | );
231 | }
232 |
233 | function get_attachment($bug) {
234 | $attachment = array('fallback' => '', 'color' => '#3AA3E3', 'mrkdwn_in' => array('pretext', 'text', 'fields'));
235 | $t_columns = (array)plugin_config_get('columns');
236 | foreach ($t_columns as $t_column) {
237 | $title = column_get_title( $t_column );
238 | $value = $this->format_value($bug, $t_column);
239 |
240 | if ($title && $value) {
241 | $attachment['fallback'] .= $title . ': ' . $value . "\n";
242 | $attachment['fields'][] = array(
243 | 'title' => $title,
244 | 'value' => $value,
245 | 'short' => $this->is_field_short($t_column),
246 | );
247 | }
248 | }
249 | return $attachment;
250 | }
251 |
252 | function is_field_short($column) {
253 | $id = custom_field_get_id_from_name(str_replace('custom_', '', $column));
254 | if ($id) {
255 | $field = custom_field_get_definition($id);
256 | return $field['type'] != CUSTOM_FIELD_TYPE_TEXTAREA;
257 | }
258 |
259 | return !column_is_extended($column);
260 | }
261 |
262 | function format_value($bug, $field_name) {
263 | $self = $this;
264 | $values = array(
265 | 'id' => function($bug) { return sprintf('<%s|%s>', string_get_bug_view_url_with_fqdn($bug->id), $bug->id); },
266 | 'project_id' => function($bug) { return project_get_name($bug->project_id); },
267 | 'reporter_id' => function($bug) { return $this->get_user_name($bug->reporter_id); },
268 | 'handler_id' => function($bug) { return empty($bug->handler_id) ? plugin_lang_get('no_user') : $this->get_user_name($bug->handler_id); },
269 | 'duplicate_id' => function($bug) { return sprintf('<%s|%s>', string_get_bug_view_url_with_fqdn($bug->duplicate_id), $bug->duplicate_id); },
270 | 'priority' => function($bug) { return get_enum_element( 'priority', $bug->priority ); },
271 | 'severity' => function($bug) { return get_enum_element( 'severity', $bug->severity ); },
272 | 'reproducibility' => function($bug) { return get_enum_element( 'reproducibility', $bug->reproducibility ); },
273 | 'status' => function($bug) { return get_enum_element( 'status', $bug->status ); },
274 | 'resolution' => function($bug) { return get_enum_element( 'resolution', $bug->resolution ); },
275 | 'projection' => function($bug) { return get_enum_element( 'projection', $bug->projection ); },
276 | 'category_id' => function($bug) { return category_full_name( $bug->category_id, false ); },
277 | 'eta' => function($bug) { return get_enum_element( 'eta', $bug->eta ); },
278 | 'view_state' => function($bug) { return $bug->view_state == VS_PRIVATE ? lang_get('private') : lang_get('public'); },
279 | 'sponsorship_total' => function($bug) { return sponsorship_format_amount( $bug->sponsorship_total ); },
280 | 'os' => function($bug) { return $bug->os; },
281 | 'os_build' => function($bug) { return $bug->os_build; },
282 | 'platform' => function($bug) { return $bug->platform; },
283 | 'version' => function($bug) { return $bug->version; },
284 | 'fixed_in_version' => function($bug) { return $bug->fixed_in_version; },
285 | 'target_version' => function($bug) { return $bug->target_version; },
286 | 'build' => function($bug) { return $bug->build; },
287 | 'summary' => function($bug) use($self) { return $self->format_summary($bug); },
288 | 'last_updated' => function($bug) { return date( config_get( 'short_date_format' ), $bug->last_updated ); },
289 | 'date_submitted' => function($bug) { return date( config_get( 'short_date_format' ), $bug->date_submitted ); },
290 | 'due_date' => function($bug) { return date( config_get( 'short_date_format' ), $bug->due_date ); },
291 | 'description' => function($bug) use($self) { return $self->format_text( $bug->description ); },
292 | 'steps_to_reproduce' => function($bug) use($self) { return $self->format_text( $bug->steps_to_reproduce ); },
293 | 'additional_information' => function($bug) use($self) { return $self->format_text( $bug->additional_information ); },
294 | );
295 | // Discover custom fields.
296 | $t_related_custom_field_ids = custom_field_get_linked_ids( $bug->project_id );
297 | foreach ( $t_related_custom_field_ids as $t_id ) {
298 | $t_def = custom_field_get_definition( $t_id );
299 | $values['custom_' . $t_def['name']] = function($bug) use ($t_id) {
300 | return custom_field_get_value( $t_id, $bug->id );
301 | };
302 | }
303 | if (isset($values[$field_name])) {
304 | $func = $values[$field_name];
305 | return $func($bug);
306 | }
307 | else {
308 | return FALSE;
309 | }
310 | }
311 |
312 | function get_channel($project) {
313 | $channels = plugin_config_get('channels');
314 | return array_key_exists($project, $channels) ? $channels[$project] : plugin_config_get('default_channel');
315 | }
316 |
317 | function get_webhook($project) {
318 | $webhooks = plugin_config_get('url_webhooks');
319 | return array_key_exists($project, $webhooks) ? $webhooks[$project] : plugin_config_get('url_webhook');
320 | }
321 |
322 | function notify($msg, $webhook, $channel, $attachment = FALSE) {
323 | if ($this->skip) return;
324 | if (empty($channel)) return;
325 | if (empty($webhook)) return;
326 |
327 | $ch = curl_init();
328 | // @see https://my.slack.com/services/new/incoming-webhook
329 | // remove istance and token and add plugin_Slack_url config , see configurations with url above
330 | $url = sprintf('%s', trim($webhook));
331 | curl_setopt($ch, CURLOPT_URL, $url);
332 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
333 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
334 | $payload = array(
335 | 'channel' => $channel,
336 | 'username' => plugin_config_get('bot_name'),
337 | 'text' => $msg,
338 | 'link_names' => plugin_config_get('link_names'),
339 | );
340 | $bot_icon = trim(plugin_config_get('bot_icon'));
341 | if (empty($bot_icon)) {
342 | $payload['icon_url'] = 'https://raw.githubusercontent.com/infojunkie/MantisBT-Slack/master/mantis_logo.png';
343 | } elseif (preg_match('/^:[a-z0-9_\-]+:$/i', $bot_icon)) {
344 | $payload['icon_emoji'] = $bot_icon;
345 | } elseif ($bot_icon) {
346 | $payload['icon_url'] = trim($bot_icon);
347 | }
348 | if ($attachment) {
349 | $payload['attachments'] = array($attachment);
350 | }
351 |
352 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
353 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
354 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
355 | curl_setopt($ch, CURLOPT_TIMEOUT, 5);
356 | $result = curl_exec($ch);
357 | if ($result !== 'ok') {
358 | trigger_error(curl_errno($ch) . ': ' . curl_error($ch), E_USER_WARNING);
359 | plugin_error('ERROR_CURL', E_USER_ERROR);
360 | }
361 | curl_close($ch);
362 | }
363 |
364 | function bbcode_to_slack($bbtext){
365 | $bbtags = array(
366 | '[b]' => '*','[/b]' => '* ',
367 | '[i]' => '_','[/i]' => '_ ',
368 | '[u]' => '_','[/u]' => '_ ',
369 | '[s]' => '~','[/s]' => '~ ',
370 | '[sup]' => '','[/sup]' => '',
371 | '[sub]' => '','[/sub]' => '',
372 |
373 | '[list]' => '','[/list]' => "\n",
374 | '[*]' => '• ',
375 |
376 | '[hr]' => "\n———\n",
377 |
378 | '[left]' => '','[/left]' => '',
379 | '[right]' => '','[/right]' => '',
380 | '[center]' => '','[/center]' => '',
381 | '[justify]' => '','[/justify]' => '',
382 | );
383 |
384 | $bbtext = str_ireplace(array_keys($bbtags), array_values($bbtags), $bbtext);
385 |
386 | $bbextended = array(
387 | "/\[code(.*?)\](.*?)\[\/code\]/is" => "```$2```",
388 | "/\[color(.*?)\](.*?)\[\/color\]/is" => "$2",
389 | "/\[size=(.*?)\](.*?)\[\/size\]/is" => "$2",
390 | "/\[highlight(.*?)\](.*?)\[\/highlight\]/is" => "$2",
391 | "/\[url](.*?)\[\/url]/i" => "<$1>",
392 | "/\[url=(.*?)\](.*?)\[\/url\]/i" => "<$1|$2>",
393 | "/\[email=(.*?)\](.*?)\[\/email\]/i" => "",
394 | "/\[img\]([^[]*)\[\/img\]/i" => "<$1>",
395 | );
396 |
397 | foreach($bbextended as $match=>$replacement){
398 | $bbtext = preg_replace($match, $replacement, $bbtext);
399 | }
400 | $bbtext = preg_replace_callback("/\[quote(=)?(.*?)\](.*?)\[\/quote\]/is",
401 | function ($matches) {
402 | if (!empty($matches[2]))
403 | $result = "\n> _*" . $matches[2] . "* wrote:_\n> \n";
404 | $lines = explode("\n", $matches[3]);
405 | foreach ($lines as $line)
406 | $result .= "> " . $line . "\n";
407 | return $result;
408 | }, $bbtext);
409 | return $bbtext;
410 | }
411 |
412 | function get_user_name($user_id) {
413 | $user = user_get_row($user_id);
414 | $username = $user['username'];
415 | $usernames = plugin_config_get('usernames');
416 | $username = array_key_exists($username, $usernames) ? $usernames[$username] : $username;
417 | return '@' . $username;
418 | }
419 | }
420 |
--------------------------------------------------------------------------------