--- zabbix-redmine.js.orig	2020-12-12 10:15:01.767753072 +0100
+++ zabbix-redmine.js	2020-12-12 08:55:52.971016471 +0100
@@ -140,6 +140,34 @@ var Redmine = {
         };
     },
 
+    getClosingStatusID: function(name) {
+        var result = Redmine.request('get', 'issue_statuses.json'),
+            status_id;
+
+        if (result.response) {
+            var statuses = result.response.issue_statuses || [];
+
+            for (var i in statuses) {
+                // Skip all statuses not closing an issue.
+                if (statuses[i].is_closed !== true) {
+                    continue;
+                }
+                // Use named status to close Zabbix-generated issues if available.
+                if (statuses[i].name === name) {
+                    status_id = statuses[i].id;
+                    break;
+                }
+                // Use first available issue closing status in any other case.
+                status_id = statuses[i].id;
+            }
+        }
+        else {
+            Zabbix.Log(4, '[ Redmine Webhook ] Failed to retrieve issue statuses.');
+        }
+
+        return status_id;
+    },
+
     getProjectID: function(name) {
         var result = Redmine.request('get', 'projects.json'),
             project_id;
@@ -165,6 +193,57 @@ var Redmine = {
         return project_id;
     },
 
+    getTrackerID: function(name) {
+        var result = Redmine.request('get', 'trackers.json'),
+            tracker_id;
+
+        if (result.response) {
+            var trackers = result.response.trackers || [];
+
+            for (var i in trackers) {
+                if (trackers[i].name === name) {
+                    tracker_id = trackers[i].id;
+                    break;
+                }
+            }
+        }
+        else {
+            Zabbix.Log(4, '[ Redmine Webhook ] Failed to retrieve tracker data.');
+        }
+
+        if (typeof tracker_id === 'undefined') {
+            throw 'Cannot find tracker with name: ' + name;
+        }
+
+        return tracker_id;
+    },
+
+    getAssigneeID: function(project, name) {
+        var result = Redmine.request('get', 'projects/' + project
+                + '/memberships.json'),
+            user_id;
+
+        if (result.response) {
+            var members = result.response.memberships || [];
+
+            for (var i in members) {
+                if (members[i].user.name === name) {
+                    user_id = members[i].user.id;
+                    break;
+                }
+            }
+        }
+        else {
+            Zabbix.Log(4, '[ Redmine Webhook ] Failed to retrieve member data.');
+        }
+
+        if (typeof user_id === 'undefined') {
+            throw 'Cannot find member with name: ' + name + ' in project ' + project;
+        }
+
+        return user_id;
+    },
+
     getSchema: function() {
         var result = Redmine.request('get', 'custom_fields.json');
 
@@ -175,10 +254,13 @@ var Redmine = {
         var project_id = /^\d+$/.test(Redmine.params.project)
                 ? Redmine.params.project
                 : Redmine.getProjectID(Redmine.params.project),
+            tracker_id = /^\d+$/.test(Redmine.params.tracker)
+                ? Redmine.params.tracker
+                : Redmine.getTrackerID(Redmine.params.tracker),
             data = {
                 issue: {
                     project_id: project_id,
-                    tracker_id: Redmine.params.tracker_id,
+                    tracker_id: tracker_id,
                     subject: subject,
                     description: description
                 }
@@ -189,6 +271,14 @@ var Redmine = {
             data.issue.priority_id = priority;
         }
 
+        if (Redmine.params.assignee !== 'null' && typeof Redmine.params.assignee !== 'undefined') {
+            data.issue.assigned_to_id = Redmine.getAssigneeID(project_id, Redmine.params.assignee);
+        }
+
+        if (Redmine.params.is_private !== 'null' && typeof Redmine.params.is_private !== 'undefined') {
+            data.issue.is_private = Redmine.params.is_private;
+        }
+
         result = Redmine.request('post', 'issues.json', Redmine.addCustomFields(data, fields));
 
         if (typeof result.response !== 'object'
@@ -200,13 +290,26 @@ var Redmine = {
         return result.response.issue.id;
     },
 
-    updateIssue: function(note, fields) {
+    updateIssue: function(note, priority, fields, closeIssue) {
         var data = {
             issue: {
                 notes: note || ''
             }
         };
 
+        if (priority) {
+            data.issue.priority_id = priority;
+        }
+
+        if (closeIssue) {
+            var status_id = /^\d+$/.test(Redmine.params.closing_status)
+                    ? Redmine.params.closing_status
+                    : Redmine.getClosingStatusID(Redmine.params.closing_status);
+            if (status_id !== 'null' && status_id !== 'undefined') {
+                data.issue.status_id = status_id;
+            }
+        }
+
         Redmine.request('put', 'issues/' + Redmine.params.issue_key + '.json', Redmine.addCustomFields(data, fields));
     }
 };
@@ -218,7 +321,7 @@ try {
         params_update = {},
         result = {tags: {}},
         required_params = [
-            'alert_subject', 'tracker_id', 'project',
+            'alert_subject', 'alert_sendto',
             'event_source', 'event_value',  'event_update_status'
         ],
         severities = [
@@ -253,6 +356,34 @@ try {
         throw 'Incorrect "event_source" parameter given: ' + params.event_source + '\nMust be 0-3.';
     }
 
+    // Check {ALERT.SENDTO} and feed project, tracker-id and assignee.
+    // Use [<assignee>@]<project>/<tracker> format, where assignee may be omitted.
+    if (params.alert_sendto !== 'null' && typeof params.alert_sendto !== 'undefined' &&
+            params.alert_sendto !== '') {
+        var send_to = params.alert_sendto.trim().split('@');
+        switch (send_to.length) {
+            case 2:
+                var assignee = send_to.shift().trim();
+                if (assignee === 'me') {
+                    params_redmine['assignee'] = params.user_fullname;
+                }
+                else {
+                    params_redmine['assignee'] = assignee;
+                }
+            case 1:
+                var target = send_to.shift().trim().split('/');
+                if (target.length != 2) {
+                    throw 'Incorrect "alert_sendto" parameter given: ' + params.alert_sendto + '\nMust be "<project>/<tracker>".';
+                }
+
+                params_redmine['project'] = target[0].trim();
+                params_redmine['tracker'] = target[1].trim();
+                Zabbix.log(4, '[ Redmine Webhook ] Using tracker \'' + params_redmine['tracker'] +
+                    '\' in project \'' + params_redmine['project'] + '\'.');
+                break;
+        }
+    }
+
     // Check {EVENT.VALUE} for trigger-based and internal events.
     if (params.event_value !== '0' && params.event_value !== '1'
         && (params.event_source === '0' || params.event_source === '3')) {
@@ -299,7 +430,7 @@ try {
     // Create issue for trigger-based events.
     else if (params.event_value === '1' && params_update.status === '0') {
         var issue_id = Redmine.createIssue(params.alert_subject,
-            params.alert_subject + '\n' + params.alert_message + '\n' +
+            params.alert_message + '\n' +
             params.zabbix_url + (params.zabbix_url.endsWith('/') ? '' : '/') +
             'tr_events.php?triggerid=' + params.trigger_id + '&eventid=' + params.event_id + '\n',
             priority,
@@ -309,9 +440,10 @@ try {
         result.tags.__zbx_redmine_issuelink = params.redmine_url +
             (params.redmine_url.endsWith('/') ? '' : '/') + 'issues/' + issue_id;
     }
-    // Update created issue for trigger-based event.
+    // Update and possibly close a trigger-based event.
     else {
-        Redmine.updateIssue(params.alert_subject + '\n' + params.alert_message, params_fields);
+        Redmine.updateIssue(params.alert_message, priority, params_fields,
+                (params.event_value === '0' && params_update.status === '0'));
     }
 
     return JSON.stringify(result);
