Display audit trail for email messages via Bonnie API (#4975)

This commit is contained in:
Thomas Bruederli 2015-04-20 16:25:48 +02:00
parent ae0765f4d9
commit de110b96a5
4 changed files with 248 additions and 3 deletions

View file

@ -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');
}
});

View file

@ -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', '');

View 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';

View 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%;
}