Display audit trail for email messages via Bonnie API (#4975)
This commit is contained in:
parent
ae0765f4d9
commit
de110b96a5
4 changed files with 248 additions and 3 deletions
|
@ -46,7 +46,7 @@ libkolab_audittrail.object_history_dialog = function(p)
|
|||
// hide and reset changelog table
|
||||
$dialog.find('div.notfound-message').remove();
|
||||
$dialog.find('.changelog-table').show().children('tbody')
|
||||
.html('<tr><td colspan="6"><span class="loading">' + rcmail.gettext('loading') + '</span></td></tr>');
|
||||
.html('<tr><td colspan="4"><span class="loading">' + rcmail.gettext('loading') + '</span></td></tr>');
|
||||
|
||||
// open jquery UI dialog
|
||||
$dialog.dialog({
|
||||
|
@ -163,7 +163,7 @@ libkolab_audittrail.render_changelog = function(data, object, folder)
|
|||
var i, change, accessible, op_append,
|
||||
first = data.length - 1, last = 0,
|
||||
is_writeable = !!folder.editable,
|
||||
op_labels = { APPEND: 'actionappend', MOVE: 'actionmove', DELETE: 'actiondelete' },
|
||||
op_labels = { RECEIVE: 'actionreceive', APPEND: 'actionappend', MOVE: 'actionmove', DELETE: 'actiondelete', READ: 'actionread', FLAGSET: 'actionflagset', FLAGCLEAR: 'actionflagclear' },
|
||||
actions = '<a href="#show" class="iconbutton preview" title="'+ rcmail.gettext('showrevision',data.module) +'" data-rev="{rev}" /> ' +
|
||||
(is_writeable ? '<a href="#restore" class="iconbutton restore" title="'+ rcmail.gettext('restore',data.module) + '" data-rev="{rev}" />' : ''),
|
||||
tbody = $dialog.find('.changelog-table tbody').html('');
|
||||
|
@ -175,6 +175,9 @@ libkolab_audittrail.render_changelog = function(data, object, folder)
|
|||
if (change.op == 'MOVE' && change.mailbox) {
|
||||
op_append = ' ⇢ ' + change.mailbox;
|
||||
}
|
||||
else if ((change.op == 'FLAGSET' || change.op == 'FLAGCLEAR') && change.flags) {
|
||||
op_append = ': ' + change.flags;
|
||||
}
|
||||
else {
|
||||
op_append = '';
|
||||
}
|
||||
|
@ -187,7 +190,7 @@ libkolab_audittrail.render_changelog = function(data, object, folder)
|
|||
.append('<td class="revision">' + Q(i+1) + '</td>')
|
||||
.append('<td class="date">' + Q(change.date || '') + '</td>')
|
||||
.append('<td class="user">' + Q(change.user || 'undisclosed') + '</td>')
|
||||
.append('<td class="operation" title="' + op_append + '">' + Q(rcmail.gettext(op_labels[change.op] || '', data.module) + (op_append ? ' ...' : '')) + '</td>')
|
||||
.append('<td class="operation" title="' + op_append + '">' + Q(rcmail.gettext(op_labels[change.op] || '', data.module) + op_append) + '</td>')
|
||||
.append('<td class="actions">' + (accessible && change.op != 'DELETE' ? actions.replace(/\{rev\}/g, change.rev) : '') + '</td>')
|
||||
.appendTo(tbody);
|
||||
}
|
||||
|
@ -210,3 +213,49 @@ libkolab_audittrail.dialog_resize = function(id, height, width)
|
|||
$(id).dialog('option', { height: Math.min(h-20, height+130), width: Math.min(w-20, width+50) })
|
||||
.dialog('option', 'position', ['center', 'center']); // only works in a separate call (!?)
|
||||
};
|
||||
|
||||
|
||||
// register handlers for mail message history
|
||||
window.rcmail && rcmail.addEventListener('init', function(e) {
|
||||
var loading_lock;
|
||||
|
||||
if (rcmail.env.task == 'mail') {
|
||||
rcmail.register_command('kolab-mail-history', function() {
|
||||
var dialog, uid = rcmail.get_single_uid(), rec = { uid: uid, mbox: rcmail.get_message_mailbox(uid) };
|
||||
if (!uid || !window.libkolab_audittrail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// render dialog
|
||||
$dialog = libkolab_audittrail.object_history_dialog({
|
||||
module: 'libkolab',
|
||||
container: '#mailmessagehistory',
|
||||
title: rcmail.gettext('objectchangelog','libkolab')
|
||||
});
|
||||
|
||||
$dialog.data('rec', rec);
|
||||
|
||||
// fetch changelog data
|
||||
loading_lock = rcmail.set_busy(true, 'loading', loading_lock);
|
||||
rcmail.http_post('plugin.message-changelog', { _uid: rec.uid, _mbox: rec.mbox }, loading_lock);
|
||||
|
||||
}, rcmail.env.action == 'show');
|
||||
|
||||
rcmail.addEventListener('plugin.message_render_changelog', function(data) {
|
||||
var $dialog = $('#mailmessagehistory'),
|
||||
rec = $dialog.data('rec');
|
||||
|
||||
if (data === false || !data.length || !event) {
|
||||
// display 'unavailable' message
|
||||
$('<div class="notfound-message dialog-message warning">' + rcmail.gettext('objectchangelognotavailable','libkolab') + '</div>')
|
||||
.insertBefore($dialog.find('.changelog-table').hide());
|
||||
return;
|
||||
}
|
||||
|
||||
data.module = 'libkolab';
|
||||
libkolab_audittrail.render_changelog(data, rec, {});
|
||||
});
|
||||
|
||||
rcmail.env.message_commands.push('kolab-mail-history');
|
||||
}
|
||||
});
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
class libkolab extends rcube_plugin
|
||||
{
|
||||
static $http_requests = array();
|
||||
static $bonnie_api = false;
|
||||
|
||||
/**
|
||||
* Required startup method of a Roundcube plugin
|
||||
|
@ -52,6 +53,32 @@ class libkolab extends rcube_plugin
|
|||
rcube::raise_error($e, true);
|
||||
kolab_format::$timezone = new DateTimeZone('GMT');
|
||||
}
|
||||
|
||||
$this->add_texts('localization/', $rcmail->output->type == 'html' && $rcmail->task == 'mail');
|
||||
|
||||
// embed scripts and templates for email message audit trail
|
||||
if ($rcmail->task == 'mail' && self::get_bonnie_api()) {
|
||||
if ($rcmail->output->type == 'html') {
|
||||
$this->add_hook('render_page', array($this, 'bonnie_render_page'));
|
||||
|
||||
$this->include_script('js/audittrail.js');
|
||||
$this->include_stylesheet($this->local_skin_path() . '/libkolab.css');
|
||||
|
||||
// add 'Show history' item to message menu
|
||||
$this->api->add_content(html::tag('li', null,
|
||||
$this->api->output->button(array(
|
||||
'command' => 'kolab-mail-history',
|
||||
'label' => 'libkolab.showhistory',
|
||||
'type' => 'link',
|
||||
'classact' => 'icon history active',
|
||||
'class' => 'icon history',
|
||||
'innerclass' => 'icon history',
|
||||
))),
|
||||
'messagemenu');
|
||||
}
|
||||
|
||||
$this->register_action('plugin.message-changelog', array($this, 'message_changelog'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,6 +90,75 @@ class libkolab extends rcube_plugin
|
|||
return $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for a singleton instance of the Bonnie API
|
||||
*
|
||||
* @return mixed kolab_bonnie_api instance if configured, false otherwise
|
||||
*/
|
||||
public static function get_bonnie_api()
|
||||
{
|
||||
// get configuration for the Bonnie API
|
||||
if (!self::$bonnie_api && ($bonnie_config = rcube::get_instance()->config->get('kolab_bonnie_api', false))) {
|
||||
self::$bonnie_api = new kolab_bonnie_api($bonnie_config);
|
||||
}
|
||||
|
||||
return self::$bonnie_api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to append the message history dialog template to the mail view
|
||||
*/
|
||||
function bonnie_render_page($p)
|
||||
{
|
||||
if (($p['template'] === 'mail' || $p['template'] === 'message') && !$p['kolab-audittrail']) {
|
||||
// append a template for the audit trail dialog
|
||||
$this->api->output->add_footer(
|
||||
html::div(array('id' => 'mailmessagehistory', 'class' => 'uidialog', 'aria-hidden' => 'true', 'style' => 'display:none'),
|
||||
self::object_changelog_table(array('class' => 'records-table changelog-table'))
|
||||
)
|
||||
);
|
||||
$this->api->output->set_env('kolab_audit_trail', true);
|
||||
$p['kolab-audittrail'] = true;
|
||||
}
|
||||
|
||||
return $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for message audit trail changelog requests
|
||||
*/
|
||||
public function message_changelog()
|
||||
{
|
||||
if (!self::$bonnie_api) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$rcmail = rcube::get_instance();
|
||||
$msguid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST, true);
|
||||
$mailbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST);
|
||||
|
||||
$result = $msguid && $mailbox ? self::$bonnie_api->changelog('mail', null, $mailbox, $msguid) : null;
|
||||
if (is_array($result)) {
|
||||
if (is_array($result['changes'])) {
|
||||
$dtformat = $rcmail->config->get('date_format') . ' ' . $rcmail->config->get('time_format');
|
||||
array_walk($result['changes'], function(&$change) use ($dtformat, $rcmail) {
|
||||
if ($change['date']) {
|
||||
$dt = rcube_utils::anytodatetime($change['date']);
|
||||
if ($dt instanceof DateTime) {
|
||||
$change['date'] = $rcmail->format_date($dt, $dtformat);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
$this->api->output->command('plugin.message_render_changelog', $result['changes']);
|
||||
}
|
||||
else {
|
||||
$this->api->output->command('plugin.message_render_changelog', false);
|
||||
}
|
||||
|
||||
$this->api->output->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function to load and initalize the HTTP_Request2 Object
|
||||
*
|
||||
|
@ -135,6 +231,7 @@ class libkolab extends rcube_plugin
|
|||
public static function object_changelog_table($attrib = array())
|
||||
{
|
||||
$rcube = rcube::get_instance();
|
||||
$attrib += array('domain' => 'libkolab');
|
||||
|
||||
$table = new html_table(array('cols' => 5, 'border' => 0, 'cellspacing' => 0));
|
||||
$table->add_header('diff', '');
|
||||
|
|
32
plugins/libkolab/localization/en_US.inc
Normal file
32
plugins/libkolab/localization/en_US.inc
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Localizations for the Kolab libkolab plugin
|
||||
*
|
||||
* Copyright (C) 2015, Kolab Systems AG
|
||||
*
|
||||
* For translation see https://www.transifex.com/projects/p/kolab/resource/libkolab/
|
||||
*/
|
||||
|
||||
$labels = array();
|
||||
|
||||
// history dialog
|
||||
$labels['showhistory'] = 'Show History';
|
||||
$labels['compare'] = 'Compare';
|
||||
$labels['objectchangelog'] = 'History';
|
||||
$labels['objectdiff'] = 'Changes from $rev1 to $rev2';
|
||||
$labels['revision'] = 'Revision';
|
||||
$labels['user'] = 'User';
|
||||
$labels['operation'] = 'Action';
|
||||
$labels['actionreceive'] = 'Received';
|
||||
$labels['actionappend'] = 'Saved';
|
||||
$labels['actionmove'] = 'Moved';
|
||||
$labels['actiondelete'] = 'Deleted';
|
||||
$labels['actionread'] = 'Marked as Read';
|
||||
$labels['actionflagset'] = 'Flag set';
|
||||
$labels['actionflagclear'] = 'Flag removed';
|
||||
$labels['showrevision'] = 'Show this version';
|
||||
$labels['restore'] = 'Restore this version';
|
||||
$labels['objectnotfound'] = 'Failed to load history data';
|
||||
$labels['objectchangelognotavailable'] = 'History is not available for this message';
|
||||
|
67
plugins/libkolab/skins/larry/libkolab.css
Normal file
67
plugins/libkolab/skins/larry/libkolab.css
Normal file
|
@ -0,0 +1,67 @@
|
|||
|
||||
ul.toolbarmenu li a.history span.history {
|
||||
background-image: url('data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAQAAAD8x0bcAAABXUlEQVQoFbXBvUsCYQAH4N/mdm5uN9UStzn1AYG0HC1Jg01tV/9A0NYQ5dRm4BI0VYRYUYOD3CSB2FaLXBJhIXYeXr7veVGd8qus0D6Whp4H+Duq1LnEBDX8jiqz3XpgPzqB3a0zSw3fUetavlux0hkjlc5cnPtut84EBlEJLr1WOoN5RKEiisTylmMHNjX0Pa17rd0TRAFqzOLN8Ma2FN4u+tpVx8Y4Xp1O+y7eaRXroUkFn7xWqYieXFwKfDjek6IxCYAqy6xJ8dBkjatALi4FDfQUN6XIxfEqPJt0bCmkMAuIAXMjx/nnBvPUAGtfip0p9ERmk45tFqDjTQix0TXrzFqg4t3cVhHBh8jECnQqVPAujAS0zqHXOshgQJhKx+ycUUcPx5j33YqFcXwRMo/a94HNMrMsd+58t1gaWsQPMSNlFq7Kvlu7LpaMFGYQwi9U6JjHEgzMQMU/eAHASL3dDo5S4wAAAABJRU5ErkJggg==');
|
||||
background-position: 0px 2px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.changelog-table .loading {
|
||||
color: #666;
|
||||
margin: 1em 0;
|
||||
padding: 1px 0 2px 24px;
|
||||
background: url('data:image/gif;base64,R0lGODlhEAAQAPQAAP///xB1v/j6/ESTzIu83xV4wDOJyNjo9K3P6CSAxH603G+s2OXv957G5Mnf8FOb0GCj1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAkKAAAALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkECQoAAAAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkECQoAAAAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAkKAAAALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAkKAAAALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==') top left no-repeat;
|
||||
}
|
||||
|
||||
.changelog-dialog .compare-button {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.changelog-table tbody td {
|
||||
padding: 4px 7px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.changelog-table tbody tr:last-child td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.changelog-table tbody tr.undisclosed td.date,
|
||||
.changelog-table tbody tr.undisclosed td.user {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.changelog-table .diff {
|
||||
width: 4em;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.changelog-table .revision {
|
||||
width: 6em;
|
||||
}
|
||||
|
||||
.changelog-table .date {
|
||||
width: 11em;
|
||||
}
|
||||
|
||||
.changelog-table .user {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.changelog-table .operation {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.changelog-table .actions {
|
||||
width: 50px;
|
||||
text-align: right;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#mailmessagehistory .changelog-table .diff,
|
||||
#mailmessagehistory .changelog-table .actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mailmessagehistory .changelog-table .operation {
|
||||
width: 25%;
|
||||
}
|
Loading…
Add table
Reference in a new issue