- Added possibility to undo last contact delete operation

release-0.6
alecpl 13 years ago
parent 632528ff42
commit 7f5a849e78

@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
- Added possibility to undo last contact delete operation
- Fix sorting of contact groups after group create (#1487747)
- Add optional textual upload progress indicator (#1486039)
- Fix parsing URLs containing commas (#1487970)

@ -507,7 +507,7 @@ class rcmail
$this->output->set_env('comm_path', $this->comm_path);
$this->output->set_charset(RCMAIL_CHARSET);
// add some basic label to client
// add some basic labels to client
$this->output->add_label('loading', 'servererror');
return $this->output;

@ -38,6 +38,7 @@ abstract class rcube_addressbook
public $primary_key;
public $groups = false;
public $readonly = true;
public $undelete = false;
public $ready = false;
public $group_id = null;
public $list_page = 1;
@ -256,7 +257,17 @@ abstract class rcube_addressbook
}
/**
* Remove all records from the database
* Unmark delete flag on contact record(s)
*
* @param array Record identifiers
*/
function undelete($ids)
{
/* empty for read-only address books */
}
/**
* Mark all records in database as deleted
*/
function delete_all()
{

@ -52,6 +52,7 @@ class rcube_contacts extends rcube_addressbook
public $primary_key = 'contact_id';
public $readonly = false;
public $groups = true;
public $undelete = true;
public $list_page = 1;
public $page_size = 10;
public $group_id = 0;
@ -691,13 +692,44 @@ class rcube_contacts extends rcube_addressbook
}
/**
* Undelete one or more contact records
*
* @param array Record identifiers
*/
function undelete($ids)
{
if (!is_array($ids))
$ids = explode(',', $ids);
$ids = $this->db->array2list($ids, 'integer');
// flag record as deleted
$this->db->query(
"UPDATE ".get_table_name($this->db_name).
" SET del=0, changed=".$this->db->now().
" WHERE user_id=?".
" AND contact_id IN ($ids)",
$this->user_id
);
$this->cache = null;
return $this->db->affected_rows();
}
/**
* Remove all records from the database
*/
function delete_all()
{
$this->db->query("DELETE FROM ".get_table_name($this->db_name)." WHERE user_id = ?", $this->user_id);
$this->cache = null;
$this->db->query("UPDATE ".get_table_name($this->db_name).
" SET del=1, changed=".$this->db->now().
" WHERE user_id = ?", $this->user_id);
return $this->db->affected_rows();
}

@ -164,14 +164,15 @@ class rcube_json_output
* @param string $type Message type [notice|confirm|error]
* @param array $vars Key-value pairs to be replaced in localized text
* @param boolean $override Override last set message
* @param int $timeout Message displaying time in seconds
* @uses self::command()
*/
public function show_message($message, $type='notice', $vars=null, $override=true)
public function show_message($message, $type='notice', $vars=null, $override=true, $timeout=0)
{
if ($override || !$this->message) {
$this->message = $message;
$msgtext = rcube_label_exists($message) ? rcube_label(array('name' => $message, 'vars' => $vars)) : $message;
$this->command('display_message', $msgtext, $type);
$this->command('display_message', $msgtext, $type, $timeout * 1000);
}
}

@ -238,18 +238,19 @@ class rcube_template extends rcube_html_page
/**
* Invoke display_message command
*
* @param string Message to display
* @param string Message type [notice|confirm|error]
* @param array Key-value pairs to be replaced in localized text
* @param boolean Override last set message
* @param string $message Message to display
* @param string $type Message type [notice|confirm|error]
* @param array $vars Key-value pairs to be replaced in localized text
* @param boolean $override Override last set message
* @param int $timeout Message display time in seconds
* @uses self::command()
*/
public function show_message($message, $type='notice', $vars=null, $override=true)
public function show_message($message, $type='notice', $vars=null, $override=true, $timeout=0)
{
if ($override || !$this->message) {
$this->message = $message;
$msgtext = rcube_label_exists($message) ? rcube_label(array('name' => $message, 'vars' => $vars)) : $message;
$this->command('display_message', $msgtext, $type);
$this->command('display_message', $msgtext, $type, $timeout * 1000);
}
}

@ -164,7 +164,7 @@ function rcube_webmail()
}
// enable general commands
this.enable_command('logout', 'mail', 'addressbook', 'settings', 'save-pref', true);
this.enable_command('logout', 'mail', 'addressbook', 'settings', 'save-pref', 'undo', true);
if (this.env.permaurl)
this.enable_command('permaurl', true);
@ -411,7 +411,7 @@ function rcube_webmail()
// show message
if (this.pending_message)
this.display_message(this.pending_message[0], this.pending_message[1]);
this.display_message(this.pending_message[0], this.pending_message[1], this.pending_message[2]);
// map implicit containers
if (this.gui_objects.folderlist)
@ -1046,6 +1046,10 @@ function rcube_webmail()
this.goto_url('settings/' + command);
break;
case 'undo':
this.http_request('undo', '', this.display_message('', 'loading'));
break;
// unified command call (command name == function name)
default:
var func = command.replace(/-/g, '_');
@ -1296,7 +1300,7 @@ function rcube_webmail()
var toffset = -moffset-boffset;
var li, div, pos, mouse, check, oldclass,
layerclass = 'draglayernormal';
if (this.contact_list && this.contact_list.draglayer)
oldclass = this.contact_list.draglayer.attr('class');
@ -4980,7 +4984,7 @@ function rcube_webmail()
if (elem._placeholder && (!$elem.val() || $elem.val() == elem._placeholder))
$elem.addClass('placeholder').attr('spellcheck', false).val(elem._placeholder);
};
// write to the document/window title
this.set_pagetitle = function(title)
{
@ -4989,27 +4993,29 @@ function rcube_webmail()
};
// display a system message, list of types in common.css (below #message definition)
this.display_message = function(msg, type)
this.display_message = function(msg, type, timeout)
{
// pass command to parent window
if (this.is_framed())
return parent.rcmail.display_message(msg, type);
return parent.rcmail.display_message(msg, type, timeout);
if (!this.gui_objects.message) {
// save message in order to display after page loaded
if (type != 'loading')
this.pending_message = new Array(msg, type);
this.pending_message = new Array(msg, type, timeout);
return false;
}
type = type ? type : 'notice';
var ref = this,
key = msg,
key = String(msg).replace(this.identifier_expr, '_'),
date = new Date(),
id = type + date.getTime(),
id = type + date.getTime();
if (!timeout)
timeout = this.message_time * (type == 'error' || type == 'warning' ? 2 : 1);
if (type == 'loading') {
key = 'loading';
timeout = this.env.request_timeout * 1000;

@ -448,6 +448,7 @@ $labels['sharedfolder'] = 'Public Folder';
$labels['sortby'] = 'Sort by';
$labels['sortasc'] = 'Sort ascending';
$labels['sortdesc'] = 'Sort descending';
$labels['undo'] = 'Undo';
// units
$labels['B'] = 'B';

@ -128,6 +128,8 @@ $messages['maxgroupmembersreached'] = 'The number of group members exceeds the m
$messages['internalerror'] = 'An internal error occured. Please try again';
$messages['contactdelerror'] = 'Could not delete contact(s)';
$messages['contactdeleted'] = 'Contact(s) deleted successfully';
$messages['contactrestoreerror'] = 'Could not restore deleted contact(s)';
$messages['contactrestored'] = 'Contact(s) restored successfully';
$messages['groupdeleted'] = 'Group deleted successfully';
$messages['grouprenamed'] = 'Group renamed successfully';
$messages['groupcreated'] = 'Group created successfully';
@ -142,5 +144,6 @@ $messages['folderupdated'] = 'Folder updated successfully';
$messages['foldercreated'] = 'Folder created successfully';
$messages['invalidimageformat'] = 'Not a valid image format';
$messages['mispellingsfound'] = 'Spelling errors detected in the message';
$messages['itemsdeleted'] = '$num item(s) has been deleted.';
?>

@ -416,5 +416,6 @@ $labels['other'] = 'Inne';
$labels['importtarget'] = 'Dodaj nowe kontakty do książki adresowej:';
$labels['grouprename'] = 'Zmień nazwę grupy';
$labels['groupdelete'] = 'Usuń grupę';
$labels['undo'] = 'Cofnij';
?>

@ -146,5 +146,8 @@ $messages['errornoperm'] = 'Nie można wykonać operacji. Brak uprawnień';
$messages['importconfirmskipped'] = '<b>Pominięto $skipped istniejących wpisów</b>';
$messages['invalidimageformat'] = 'Niepoprawny format obrazka';
$messages['mispellingsfound'] = 'Wykryto błędy pisowni w tej wiadomości';
$messages['itemsdeleted'] = '$num elemenów zostało usuniętych.';
$messages['contactrestoreerror'] = 'Przywracanie kontaktów nie powiodło się';
$messages['contactrestored'] = 'Kontakt(y) zostały przywrócone';
?>

@ -26,6 +26,9 @@ if (!$OUTPUT->ajax_call)
$cids = rcmail_get_cids();
$delcnt = 0;
// remove previous deletes
$RCMAIL->session->remove('contact_undo');
foreach ($cids as $source => $cid)
{
$CONTACTS = rcmail_contact_source($source);
@ -36,6 +39,7 @@ foreach ($cids as $source => $cid)
if (count($cids) == 1) {
$OUTPUT->show_message('contactdelerror', 'error');
$OUTPUT->command('list_contacts');
$OUTPUT->send();
}
continue;
}
@ -52,11 +56,14 @@ foreach ($cids as $source => $cid)
}
else {
$delcnt += $deleted;
// store deleted contacts IDs in session for undelete
if ($CONTACTS->undelete) {
$_SESSION['contact_undo']['data'][$source] = $cid;
}
}
}
$OUTPUT->show_message('contactdeleted', 'confirmation');
$page = isset($_SESSION['page']) ? $_SESSION['page'] : 1;
// update saved search after data changed
@ -135,6 +142,17 @@ else {
$OUTPUT->set_env('pagecount', ceil($result->count / $CONFIG['pagesize']));
$OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($result));
if (!empty($_SESSION['contact_undo'])) {
$_SESSION['contact_undo']['ts'] = time();
$msg = html::span(null, rcube_label(array('name' => 'itemsdeleted', 'vars' => array('num' => $deleted))))
. ' ' . html::a(array('onclick' => JS_OBJECT_NAME.".command('undo', '', this)"), rcube_label('undo'));
$OUTPUT->show_message($msg, 'confirmation', null, true, $RCMAIL->config->get('undo_timeout', 15));
}
else {
$OUTPUT->show_message('contactdeleted', 'confirmation');
}
// add new rows from next page (if any)
if (!empty($records)) {
rcmail_js_contacts_list($records);

@ -88,6 +88,12 @@ if (!$RCMAIL->action && !$OUTPUT->ajax_call) {
$CONTACTS = rcmail_contact_source($source, true);
}
// remove undo information...
if ($undo = $_SESSION['contact_undo']) {
// ...after 30 seconds
if ($undo['ts'] < time() - 30)
$RCMAIL->session->remove('contact_undo');
}
// instantiate a contacts object according to the given source
function rcmail_contact_source($source=null, $init_env=false)

@ -0,0 +1,87 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/addressbook/undo.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2011, Kolab Systems AG |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Undelete contacts (CIDs) from last delete action |
| |
+-----------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+-----------------------------------------------------------------------+
$Id$
*/
// process ajax requests only
if (!$OUTPUT->ajax_call)
return;
$undo = $_SESSION['contact_undo'];
$delcnt = 0;
foreach ((array)$undo['data'] as $source => $cid)
{
$CONTACTS = rcmail_contact_source($source);
$plugin = $RCMAIL->plugins->exec_hook('contact_undelete', array(
'id' => $cid, 'source' => $source));
$restored = !$plugin['abort'] ? $CONTACTS->undelete($cid) : $plugin['result'];
if (!$restored) {
$OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'contactrestoreerror', 'error');
$OUTPUT->command('list_contacts');
$OUTPUT->send();
}
else {
$delcnt += $restored;
}
}
// update saved search after data changed
if ($delcnt && ($search_request = $_REQUEST['_search']) && isset($_SESSION['search'][$search_request])) {
$search = (array)$_SESSION['search'][$search_request];
foreach ($search as $s => $set) {
$source = $RCMAIL->get_address_book($s);
// reset page
$source->set_page(1);
$source->set_pagesize(9999);
$source->set_search_set($set);
// get records
$result = $source->list_records(array('name', 'email'));
if (!$result->count) {
unset($search[$s]);
continue;
}
while ($row = $result->next()) {
$row['sourceid'] = $s;
$key = $row['name'] . ':' . $row['sourceid'];
$records[$key] = $row;
}
unset($result);
$search[$s] = $source->get_search_set();
}
$_SESSION['search'][$search_request] = $search;
}
$RCMAIL->session->remove('contact_undo');
$OUTPUT->show_message('contactrestored', 'confirmation');
$OUTPUT->command('list_contacts');
// send response
$OUTPUT->send();

@ -252,6 +252,12 @@ img
border: 1px solid #CCCCCC;
}
#message a
{
cursor: pointer;
text-decoration: underline;
}
.box
{
border: 1px solid #999;

@ -196,6 +196,6 @@
<roundcube:button command="menu-save" id="listmenusave" type="input" class="button mainaction" label="save" />
</div>
</div>
<div id="undelete-message">11 item(s) has been deleted. <a href="#">Undo</a></div>
</body>
</html>

Loading…
Cancel
Save