Finish contact audit trail (#4972) with full display and restoring of old revisions

This commit is contained in:
Thomas Bruederli 2015-04-17 11:03:06 +02:00
parent 38246558b3
commit 6f948d0467
7 changed files with 169 additions and 17 deletions

View file

@ -364,18 +364,49 @@ rcube_webmail.prototype.contact_history_dialog = function()
return false;
}
if (this.contact_list && this.contact_list.data[rec.cid]) {
$.extend(rec, this.contact_list.data[rec.cid]);
}
// render dialog
$dialog = libkolab_audittrail.object_history_dialog({
module: 'kolab_addressbooks',
module: 'kolab_addressbook',
container: '#contacthistory',
title: rcmail.gettext('objectchangelog','kolab_addressbook'),
title: rcmail.gettext('objectchangelog','kolab_addressbook') + ' - ' + rec.name,
// callback function for list actions
listfunc: function(action, rev) {
var rec = $dialog.data('rec');
console.log(action, rev, rec)
//rcmail.loading_lock = rcmail.set_busy(true, 'loading', this.loading_lock);
//rcmail.http_post('action', { _do: action, _data: { uid: rec.uid, list:rec.list, rev: rev } }, saving_lock);
if (action == 'show') {
// open contact view in a dialog (iframe)
var dialog, iframe = $('<iframe>')
.attr('id', 'contactshowrevframe')
.attr('width', '100%')
.attr('height', '98%')
.attr('frameborder', '0')
.attr('src', rcmail.url('show', { _cid: rec.cid, _source: rec.source, _rev: rev, _framed: 1 })),
contentframe = $('#' + rcmail.env.contentframe)
// open jquery UI dialog
dialog = rcmail.show_popup_dialog(iframe, '', {}, {
modal: false,
resizable: true,
closeOnEscape: true,
title: rec.name + ' @ ' + rev,
close: function() {
dialog.dialog('destroy').attr('aria-hidden', 'true').remove();
},
minWidth: 400,
width: contentframe.width() || 600,
height: contentframe.height() || 400
});
dialog.css('padding', '0');
}
else {
rcmail.kab_loading_lock = rcmail.set_busy(true, 'loading', rcmail.kab_loading_lock);
rcmail.http_post('plugin.contact-' + action, { cid: rec.cid, source: rec.source, rev: rev }, rcmail.kab_loading_lock);
}
},
// callback function for comparing two object revisions
@ -407,13 +438,10 @@ rcube_webmail.prototype.contact_render_changelog = function(data)
}
source = this.env.address_sources[rec.source] || {}
// source.editable = !source.readonly
source.editable = !source.readonly
data.module = 'kolab_addressbook';
libkolab_audittrail.render_changelog(data, rec, source);
// set dialog size according to content
// dialog_resize($dialog.get(0), $dialog.height(), 600);
};
// callback for rendering a diff view of two contact revisions
@ -519,8 +547,24 @@ rcube_webmail.prototype.contact_show_diff = function(data)
width: 480
}).show();
// set dialog size according to content
// dialog_resize($dialog.get(0), $dialog.height(), rcmail.gui_containers.notedetailview.width() - 40);
// set dialog size according to content frame
libkolab_audittrail.dialog_resize($dialog.get(0), $dialog.height(), ($('#' + rcmail.env.contentframe).width() || 440) - 40);
};
// close the contact history dialog
rcube_webmail.prototype.close_contact_history_dialog = function(refresh)
{
$('#contacthistory, #contactdiff').each(function(i, elem) {
var $dialog = $(elem);
if ($dialog.is(':ui-dialog'))
$dialog.dialog('close');
});
// reload the contact content frame
if (refresh && this.get_single_cid() == refresh) {
this.load_contact(refresh, 'show', true);
}
};
@ -541,6 +585,21 @@ function kolab_addressbook_contextmenu()
}
});
}
else if (menu.menu_name == 'contactlist' && rcmail.env.kolab_audit_trail) {
// add "Show History" item to context menu
menu.menu_source.push({
label: rcmail.get_label('kolab_addressbook.showhistory'),
command: 'contact_history_dialog',
classes: 'history'
});
// enable history item if the contact source supports it
menu.addEventListener('activate', function(p) {
if (p.command == 'contact_history_dialog') {
var source = rcmail.env.address_sources ? rcmail.env.address_sources[rcmail.env.source] : {};
return !!source.audittrail;
}
});
}
});
}

View file

@ -63,6 +63,7 @@ class kolab_addressbook extends rcube_plugin
if ($this->rc->task == 'addressbook') {
$this->add_texts('localization');
$this->add_hook('contact_form', array($this, 'contact_form'));
$this->add_hook('contact_photo', array($this, 'contact_photo'));
$this->add_hook('template_object_directorylist', array($this, 'directorylist_html'));
// Plugin actions
@ -73,7 +74,7 @@ class kolab_addressbook extends rcube_plugin
$this->register_action('plugin.contact-changelog', array($this, 'contact_changelog'));
$this->register_action('plugin.contact-diff', array($this, 'contact_diff'));
$this->register_action('plugin.contact-show', array($this, 'contact_show'));
$this->register_action('plugin.contact-restore', array($this, 'contact_restore'));
// get configuration for the Bonnie API
if ($bonnie_config = $this->rc->config->get('kolab_bonnie_api', false)) {
@ -509,13 +510,26 @@ class kolab_addressbook extends rcube_plugin
*/
}
if ($this->bonnie_api && $this->rc->action == 'show') {
if ($this->bonnie_api && $this->rc->action == 'show' && empty($p['record']['rev'])) {
$this->rc->output->set_env('kolab_audit_trail', true);
}
return $p;
}
/**
* Plugin hook for the contact photo image
*/
public function contact_photo($p)
{
// add photo data from old revision inline as data url
if (!empty($p['record']['rev']) && !empty($p['data'])) {
$p['url'] = 'data:image/gif;base64,' . base64_encode($p['data']);
}
return $p;
}
/**
* Handler for contact audit trail changelog requests
*/
@ -677,21 +691,80 @@ class kolab_addressbook extends rcube_plugin
}
/**
* Handler for audit trail revision view requests
* Handler for audit trail revision restore requests
*/
public function contact_show()
public function contact_restore()
{
if (empty($this->bonnie_api)) {
return false;
}
$success = false;
$contact = rcube_utils::get_input_value('cid', rcube_utils::INPUT_POST, true);
$source = rcube_utils::get_input_value('source', rcube_utils::INPUT_POST);
$rev = rcube_utils::get_input_value('rev', rcube_utils::INPUT_POST);
list($uid, $mailbox, $msguid) = $this->_resolve_contact_identity($contact, $source, $folder);
if ($folder && ($raw_msg = $this->bonnie_api->rawdata('contact', $uid, $rev, $mailbox))) {
$imap = $this->rc->get_storage();
// insert $raw_msg as new message
if ($imap->save_message($folder->name, $raw_msg, null, false)) {
$success = true;
// delete old revision from imap and cache
$imap->delete_message($msguid, $folder->name);
$folder->cache->set($msguid, false);
$this->cache = array();
}
}
if ($success) {
$this->rc->output->command('display_message', $this->gettext(array('name' => 'objectrestoresuccess', 'vars' => array('rev' => $rev))), 'confirmation');
$this->rc->output->command('close_contact_history_dialog', $contact);
}
else {
$this->rc->output->command('display_message', $this->gettext('objectrestoreerror'), 'error');
}
$this->rc->output->send();
}
/**
* Get a previous revision of the given contact record from the Bonnie API
*/
public function get_revision($cid, $source, $rev)
{
if (empty($this->bonnie_api)) {
return false;
}
list($uid, $mailbox, $msguid) = $this->_resolve_contact_identity($cid, $source);
// call Bonnie API
$result = $this->bonnie_api->get('contact', $uid, $rev, $mailbox, $msguid);
if (is_array($result) && $result['uid'] == $uid && !empty($result['xml'])) {
$format = kolab_format::factory('contact');
$format->load($result['xml']);
$rec = $format->to_array();
if ($format->is_valid()) {
$rec['rev'] = $result['rev'];
return $rec;
}
}
return false;
}
/**
* Helper method to resolved the given contact identifier into uid and mailbox
*
* @return array (uid,mailbox,msguid) tuple
*/
private function _resolve_contact_identity($id, $abook)
private function _resolve_contact_identity($id, $abook, &$folder = null)
{
$mailbox = $msguid = null;

View file

@ -108,6 +108,7 @@ class kolab_addressbook_ui
if ($this->plugin->bonnie_api) {
$this->rc->output->set_env('kolab_audit_trail', true);
$this->plugin->api->include_script('libkolab/js/audittrail.js');
$this->rc->output->add_label(
@ -121,6 +122,7 @@ class kolab_addressbook_ui
'kolab_addressbook.actiondelete',
'kolab_addressbook.objectdiffnotavailable',
'kolab_addressbook.objectchangelognotavailable',
'kolab_addressbook.revisionrestoreconfirm',
'close'
);

View file

@ -554,11 +554,22 @@ class rcube_kolab_contacts extends rcube_addressbook
{
$rec = null;
$uid = $this->id2uid($id);
$rev = rcube_utils::get_input_value('_rev', rcube_utils::INPUT_GPC);
if (strpos($uid, 'mailto:') === 0) {
$this->_fetch_groups(true);
$rec = $this->contacts[$id];
$this->readonly = true; // set source to read-only
}
else if (!empty($rev)) {
$rcmail = rcube::get_instance();
$plugin = $rcmail->plugins->get_plugin('kolab_addressbook');
if ($plugin && ($object = $plugin->get_revision($id, kolab_storage::id_encode($this->imap_folder), $rev))) {
$rec = $this->_to_rcube_contact($object);
$rec['rev'] = $rev;
}
$this->readonly = true; // set source to read-only
}
else if ($object = $this->storagefolder->get_object($uid)) {
$rec = $this->_to_rcube_contact($object);
}

View file

@ -61,9 +61,12 @@ $labels['actionmove'] = 'Moved';
$labels['actiondelete'] = 'Deleted';
$labels['showrevision'] = 'Show this version';
$labels['restore'] = 'Restore this version';
$labels['revisionrestoreconfirm'] = 'Do you really want to restore revision $rev of this contact? This will replace the current contact with the old version.';
$labels['objectnotfound'] = 'Failed to load contact data';
$labels['objectchangelognotavailable'] = 'Change history is not available for this contact';
$labels['objectdiffnotavailable'] = 'No comparison possible for the selected revisions';
$labels['objectrestoresuccess'] = 'Revision $rev successfully restored';
$labels['objectrestoreerror'] = 'Failed to restore the old revision';
$messages['bookdeleteconfirm'] = 'Do you really want to delete the selected address book and all contacts in it?';
$messages['bookdeleting'] = 'Deleting address book...';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -244,3 +244,7 @@
content: ")";
}
div.contextmenu.rcmmainmenu ul.iconized li a.history > span.icon {
background: url(folder_icons.png) 1px -214px no-repeat;
}