Mercurial > hg > rc1
diff plugins/archive/archive.php @ 0:1e000243b222
vanilla 1.3.3 distro, I hope
author | Charlie Root |
---|---|
date | Thu, 04 Jan 2018 15:50:29 -0500 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/archive/archive.php Thu Jan 04 15:50:29 2018 -0500 @@ -0,0 +1,456 @@ +<?php + +/** + * Archive + * + * Plugin that adds a new button to the mailbox toolbar + * to move messages to a (user selectable) archive folder. + * + * @version 3.0 + * @license GNU GPLv3+ + * @author Andre Rodier, Thomas Bruederli, Aleksander Machniak + */ +class archive extends rcube_plugin +{ + public $task = 'settings|mail'; + + + function init() + { + $rcmail = rcmail::get_instance(); + + // register special folder type + rcube_storage::$folder_types[] = 'archive'; + + if ($rcmail->task == 'mail' && ($rcmail->action == '' || $rcmail->action == 'show') + && ($archive_folder = $rcmail->config->get('archive_mbox')) + ) { + $skin_path = $this->local_skin_path(); + if (is_file($this->home . "/$skin_path/archive.css")) { + $this->include_stylesheet("$skin_path/archive.css"); + } + + $this->include_script('archive.js'); + $this->add_texts('localization', true); + $this->add_button( + array( + 'type' => 'link', + 'label' => 'buttontext', + 'command' => 'plugin.archive', + 'class' => 'button buttonPas archive disabled', + 'classact' => 'button archive', + 'width' => 32, + 'height' => 32, + 'title' => 'buttontitle', + 'domain' => $this->ID, + ), + 'toolbar'); + + // register hook to localize the archive folder + $this->add_hook('render_mailboxlist', array($this, 'render_mailboxlist')); + + // set env variables for client + $rcmail->output->set_env('archive_folder', $archive_folder); + $rcmail->output->set_env('archive_type', $rcmail->config->get('archive_type','')); + } + else if ($rcmail->task == 'mail') { + // handler for ajax request + $this->register_action('plugin.move2archive', array($this, 'move_messages')); + } + else if ($rcmail->task == 'settings') { + $this->add_hook('preferences_list', array($this, 'prefs_table')); + $this->add_hook('preferences_save', array($this, 'save_prefs')); + } + } + + /** + * Hook to give the archive folder a localized name in the mailbox list + */ + function render_mailboxlist($p) + { + $rcmail = rcmail::get_instance(); + $archive_folder = $rcmail->config->get('archive_mbox'); + $show_real_name = $rcmail->config->get('show_real_foldernames'); + + // set localized name for the configured archive folder + if ($archive_folder && !$show_real_name) { + if (isset($p['list'][$archive_folder])) { + $p['list'][$archive_folder]['name'] = $this->gettext('archivefolder'); + } + else { + // search in subfolders + $this->_mod_folder_name($p['list'], $archive_folder, $this->gettext('archivefolder')); + } + } + + return $p; + } + + /** + * Helper method to find the archive folder in the mailbox tree + */ + private function _mod_folder_name(&$list, $folder, $new_name) + { + foreach ($list as $idx => $item) { + if ($item['id'] == $folder) { + $list[$idx]['name'] = $new_name; + return true; + } + else if (!empty($item['folders'])) { + if ($this->_mod_folder_name($list[$idx]['folders'], $folder, $new_name)) { + return true; + } + } + } + + return false; + } + + /** + * Plugin action to move the submitted list of messages to the archive subfolders + * according to the user settings and their headers. + */ + function move_messages() + { + $rcmail = rcmail::get_instance(); + + // only process ajax requests + if (!$rcmail->output->ajax_call) { + return; + } + + $this->add_texts('localization'); + + $storage = $rcmail->get_storage(); + $delimiter = $storage->get_hierarchy_delimiter(); + $read_on_move = (bool) $rcmail->config->get('read_on_archive'); + $archive_type = $rcmail->config->get('archive_type', ''); + $archive_folder = $rcmail->config->get('archive_mbox'); + $archive_prefix = $archive_folder . $delimiter; + $current_mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST); + $search_request = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GPC); + $uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST); + + // count messages before changing anything + if ($_POST['_from'] != 'show') { + $threading = (bool) $storage->get_threading(); + $old_count = $storage->count(null, $threading ? 'THREADS' : 'ALL'); + $old_pages = ceil($old_count / $storage->get_pagesize()); + } + + $count = 0; + + // this way response handler for 'move' action will be executed + $rcmail->action = 'move'; + $this->result = array( + 'reload' => false, + 'error' => false, + 'sources' => array(), + 'destinations' => array(), + ); + + foreach (rcmail::get_uids(null, null, $multifolder) as $mbox => $uids) { + if (!$archive_folder || strpos($mbox, $archive_prefix) === 0) { + $count = count($uids); + continue; + } + else if (!$archive_type || $archive_type == 'folder') { + $folder = $archive_folder; + + if ($archive_type == 'folder') { + // compose full folder path + $folder .= $delimiter . $mbox; + + // create archive subfolder if it doesn't yet exist + $this->subfolder_worker($folder); + } + + $count += $this->move_messages_worker($uids, $mbox, $folder, $read_on_move); + } + else { + if ($uids == '*') { + $index = $storage->index(null, rcmail_sort_column(), rcmail_sort_order()); + $uids = $index->get(); + } + + $messages = $storage->fetch_headers($mbox, $uids); + $execute = array(); + + foreach ($messages as $message) { + $subfolder = null; + switch ($archive_type) { + case 'year': + $subfolder = $rcmail->format_date($message->timestamp, 'Y'); + break; + + case 'month': + $subfolder = $rcmail->format_date($message->timestamp, 'Y') + . $delimiter . $rcmail->format_date($message->timestamp, 'm'); + break; + + case 'sender': + $from = $message->get('from'); + preg_match('/[\b<](.+@.+)[\b>]/i', $from, $m); + $subfolder = $m[1] ?: $this->gettext('unkownsender'); + + // replace reserved characters in folder name + $repl = $delimiter == '-' ? '_' : '-'; + $replacements[$delimiter] = $repl; + $replacements['.'] = $repl; // some IMAP server do not allow . characters + $subfolder = strtr($subfolder, $replacements); + break; + } + + // compose full folder path + $folder = $archive_folder . ($subfolder ? $delimiter . $subfolder : ''); + + $execute[$folder][] = $message->uid; + } + + foreach ($execute as $folder => $uids) { + // create archive subfolder if it doesn't yet exist + $this->subfolder_worker($folder); + + $count += $this->move_messages_worker($uids, $mbox, $folder, $read_on_move); + } + } + } + + if ($this->result['error']) { + if ($_POST['_from'] != 'show') { + $rcmail->output->command('list_mailbox'); + } + + $rcmail->output->show_message($this->gettext('archiveerror'), 'warning'); + $rcmail->output->send(); + } + + if (!empty($_POST['_refresh'])) { + // FIXME: send updated message rows instead of reloading the entire list + $rcmail->output->command('refresh_list'); + } + else { + $addrows = true; + } + + // refresh saved search set after moving some messages + if ($search_request && $rcmail->storage->get_search_set()) { + $_SESSION['search'] = $rcmail->storage->refresh_search(); + } + + if ($_POST['_from'] == 'show') { + if ($next = rcube_utils::get_input_value('_next_uid', rcube_utils::INPUT_GPC)) { + $rcmail->output->command('show_message', $next); + } + else { + $rcmail->output->command('command', 'list'); + } + + $rcmail->output->send(); + } + + $mbox = $storage->get_folder(); + $msg_count = $storage->count(null, $threading ? 'THREADS' : 'ALL'); + $exists = $storage->count($mbox, 'EXISTS', true); + $page_size = $storage->get_pagesize(); + $page = $storage->get_page(); + $pages = ceil($msg_count / $page_size); + $nextpage_count = $old_count - $page_size * $page; + $remaining = $msg_count - $page_size * ($page - 1); + + // jump back one page (user removed the whole last page) + if ($page > 1 && $remaining == 0) { + $page -= 1; + $storage->set_page($page); + $_SESSION['page'] = $page; + $jump_back = true; + } + + // update message count display + $rcmail->output->set_env('messagecount', $msg_count); + $rcmail->output->set_env('current_page', $page); + $rcmail->output->set_env('pagecount', $pages); + $rcmail->output->set_env('exists', $exists); + + // update mailboxlist + $unseen_count = $msg_count ? $storage->count($mbox, 'UNSEEN') : 0; + $old_unseen = rcmail_get_unseen_count($mbox); + $quota_root = $multifolder ? $this->result['sources'][0] : 'INBOX'; + + if ($old_unseen != $unseen_count) { + $rcmail->output->command('set_unread_count', $mbox, $unseen_count, ($mbox == 'INBOX')); + rcmail_set_unseen_count($mbox, $unseen_count); + } + + $rcmail->output->command('set_quota', $rcmail->quota_content(null, $quota_root)); + $rcmail->output->command('set_rowcount', rcmail_get_messagecount_text($msg_count), $mbox); + + if ($threading) { + $count = rcube_utils::get_input_value('_count', rcube_utils::INPUT_POST); + } + + // add new rows from next page (if any) + if ($addrows && $count && $uids != '*' && ($jump_back || $nextpage_count > 0)) { + // #5862: Don't add more rows than it was on the next page + $count = $jump_back ? null : min($nextpage_count, $count); + + $a_headers = $storage->list_messages($mbox, null, + rcmail_sort_column(), rcmail_sort_order(), $count); + + rcmail_js_message_list($a_headers, false); + } + + if ($this->result['reload']) { + $rcmail->output->show_message($this->gettext('archivedreload'), 'confirmation'); + } + else { + $rcmail->output->show_message($this->gettext('archived'), 'confirmation'); + + if (!$read_on_move) { + foreach ($this->result['destinations'] as $folder) { + rcmail_send_unread_count($folder, true); + } + } + } + + // send response + $rcmail->output->send(); + } + + /** + * Move messages from one folder to another and mark as read if needed + */ + private function move_messages_worker($uids, $from_mbox, $to_mbox, $read_on_move) + { + $storage = rcmail::get_instance()->get_storage(); + + if ($read_on_move) { + // don't flush cache (4th argument) + $storage->set_flag($uids, 'SEEN', $from_mbox, true); + } + + // move message to target folder + if ($storage->move_message($uids, $to_mbox, $from_mbox)) { + if (!in_array($from_mbox, $this->result['sources'])) { + $this->result['sources'][] = $from_mbox; + } + if (!in_array($to_mbox, $this->result['destinations'])) { + $this->result['destinations'][] = $to_mbox; + } + + return count($uids); + } + + $this->result['error'] = true; + } + + /** + * Create archive subfolder if it doesn't yet exist + */ + private function subfolder_worker($folder) + { + $storage = rcmail::get_instance()->get_storage(); + $delimiter = $storage->get_hierarchy_delimiter(); + + if ($this->folders === null) { + $this->folders = $storage->list_folders('', $archive_folder . '*', 'mail', null, true); + } + + if (!in_array($folder, $this->folders)) { + $path = explode($delimiter, $folder); + + // we'll create all folders in the path + for ($i=0; $i<count($path); $i++) { + $_folder = implode($delimiter, array_slice($path, 0, $i+1)); + if (!in_array($_folder, $this->folders)) { + if ($storage->create_folder($_folder, true)) { + $this->result['reload'] = true; + $this->folders[] = $_folder; + } + } + } + } + } + + /** + * Hook to inject plugin-specific user settings + */ + function prefs_table($args) + { + global $CURR_SECTION; + + $this->add_texts('localization'); + + $rcmail = rcmail::get_instance(); + $dont_override = $rcmail->config->get('dont_override', array()); + + if ($args['section'] == 'folders' && !in_array('archive_mbox', $dont_override)) { + $mbox = $rcmail->config->get('archive_mbox'); + $type = $rcmail->config->get('archive_type'); + + // load folders list when needed + if ($CURR_SECTION) { + $select = $rcmail->folder_selector(array( + 'noselection' => '---', + 'realnames' => true, + 'maxlength' => 30, + 'folder_filter' => 'mail', + 'folder_rights' => 'w', + 'onchange' => "if ($(this).val() == 'INBOX') $(this).val('')", + )); + } + else { + $select = new html_select(); + } + + $args['blocks']['main']['options']['archive_mbox'] = array( + 'title' => $this->gettext('archivefolder'), + 'content' => $select->show($mbox, array('name' => "_archive_mbox")) + ); + + // add option for structuring the archive folder + $archive_type = new html_select(array('name' => '_archive_type', 'id' => 'ff_archive_type')); + $archive_type->add($this->gettext('none'), ''); + $archive_type->add($this->gettext('archivetypeyear'), 'year'); + $archive_type->add($this->gettext('archivetypemonth'), 'month'); + $archive_type->add($this->gettext('archivetypesender'), 'sender'); + $archive_type->add($this->gettext('archivetypefolder'), 'folder'); + + $args['blocks']['archive'] = array( + 'name' => rcube::Q($this->gettext('settingstitle')), + 'options' => array('archive_type' => array( + 'title' => $this->gettext('archivetype'), + 'content' => $archive_type->show($type) + ) + ) + ); + } + else if ($args['section'] == 'server' && !in_array('read_on_archive', $dont_override)) { + $chbox = new html_checkbox(array('name' => '_read_on_archive', 'id' => 'ff_read_on_archive', 'value' => 1)); + $args['blocks']['main']['options']['read_on_archive'] = array( + 'title' => $this->gettext('readonarchive'), + 'content' => $chbox->show($rcmail->config->get('read_on_archive') ? 1 : 0) + ); + } + + return $args; + } + + /** + * Hook to save plugin-specific user settings + */ + function save_prefs($args) + { + $rcmail = rcmail::get_instance(); + $dont_override = $rcmail->config->get('dont_override', array()); + + if ($args['section'] == 'folders' && !in_array('archive_mbox', $dont_override)) { + $args['prefs']['archive_type'] = rcube_utils::get_input_value('_archive_type', rcube_utils::INPUT_POST); + } + else if ($args['section'] == 'server' && !in_array('read_on_archive', $dont_override)) { + $args['prefs']['read_on_archive'] = (bool) rcube_utils::get_input_value('_read_on_archive', rcube_utils::INPUT_POST); + } + + return $args; + } +}