Mercurial > hg > rc1
comparison 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 |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:1e000243b222 |
|---|---|
| 1 <?php | |
| 2 | |
| 3 /** | |
| 4 * Archive | |
| 5 * | |
| 6 * Plugin that adds a new button to the mailbox toolbar | |
| 7 * to move messages to a (user selectable) archive folder. | |
| 8 * | |
| 9 * @version 3.0 | |
| 10 * @license GNU GPLv3+ | |
| 11 * @author Andre Rodier, Thomas Bruederli, Aleksander Machniak | |
| 12 */ | |
| 13 class archive extends rcube_plugin | |
| 14 { | |
| 15 public $task = 'settings|mail'; | |
| 16 | |
| 17 | |
| 18 function init() | |
| 19 { | |
| 20 $rcmail = rcmail::get_instance(); | |
| 21 | |
| 22 // register special folder type | |
| 23 rcube_storage::$folder_types[] = 'archive'; | |
| 24 | |
| 25 if ($rcmail->task == 'mail' && ($rcmail->action == '' || $rcmail->action == 'show') | |
| 26 && ($archive_folder = $rcmail->config->get('archive_mbox')) | |
| 27 ) { | |
| 28 $skin_path = $this->local_skin_path(); | |
| 29 if (is_file($this->home . "/$skin_path/archive.css")) { | |
| 30 $this->include_stylesheet("$skin_path/archive.css"); | |
| 31 } | |
| 32 | |
| 33 $this->include_script('archive.js'); | |
| 34 $this->add_texts('localization', true); | |
| 35 $this->add_button( | |
| 36 array( | |
| 37 'type' => 'link', | |
| 38 'label' => 'buttontext', | |
| 39 'command' => 'plugin.archive', | |
| 40 'class' => 'button buttonPas archive disabled', | |
| 41 'classact' => 'button archive', | |
| 42 'width' => 32, | |
| 43 'height' => 32, | |
| 44 'title' => 'buttontitle', | |
| 45 'domain' => $this->ID, | |
| 46 ), | |
| 47 'toolbar'); | |
| 48 | |
| 49 // register hook to localize the archive folder | |
| 50 $this->add_hook('render_mailboxlist', array($this, 'render_mailboxlist')); | |
| 51 | |
| 52 // set env variables for client | |
| 53 $rcmail->output->set_env('archive_folder', $archive_folder); | |
| 54 $rcmail->output->set_env('archive_type', $rcmail->config->get('archive_type','')); | |
| 55 } | |
| 56 else if ($rcmail->task == 'mail') { | |
| 57 // handler for ajax request | |
| 58 $this->register_action('plugin.move2archive', array($this, 'move_messages')); | |
| 59 } | |
| 60 else if ($rcmail->task == 'settings') { | |
| 61 $this->add_hook('preferences_list', array($this, 'prefs_table')); | |
| 62 $this->add_hook('preferences_save', array($this, 'save_prefs')); | |
| 63 } | |
| 64 } | |
| 65 | |
| 66 /** | |
| 67 * Hook to give the archive folder a localized name in the mailbox list | |
| 68 */ | |
| 69 function render_mailboxlist($p) | |
| 70 { | |
| 71 $rcmail = rcmail::get_instance(); | |
| 72 $archive_folder = $rcmail->config->get('archive_mbox'); | |
| 73 $show_real_name = $rcmail->config->get('show_real_foldernames'); | |
| 74 | |
| 75 // set localized name for the configured archive folder | |
| 76 if ($archive_folder && !$show_real_name) { | |
| 77 if (isset($p['list'][$archive_folder])) { | |
| 78 $p['list'][$archive_folder]['name'] = $this->gettext('archivefolder'); | |
| 79 } | |
| 80 else { | |
| 81 // search in subfolders | |
| 82 $this->_mod_folder_name($p['list'], $archive_folder, $this->gettext('archivefolder')); | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 return $p; | |
| 87 } | |
| 88 | |
| 89 /** | |
| 90 * Helper method to find the archive folder in the mailbox tree | |
| 91 */ | |
| 92 private function _mod_folder_name(&$list, $folder, $new_name) | |
| 93 { | |
| 94 foreach ($list as $idx => $item) { | |
| 95 if ($item['id'] == $folder) { | |
| 96 $list[$idx]['name'] = $new_name; | |
| 97 return true; | |
| 98 } | |
| 99 else if (!empty($item['folders'])) { | |
| 100 if ($this->_mod_folder_name($list[$idx]['folders'], $folder, $new_name)) { | |
| 101 return true; | |
| 102 } | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 return false; | |
| 107 } | |
| 108 | |
| 109 /** | |
| 110 * Plugin action to move the submitted list of messages to the archive subfolders | |
| 111 * according to the user settings and their headers. | |
| 112 */ | |
| 113 function move_messages() | |
| 114 { | |
| 115 $rcmail = rcmail::get_instance(); | |
| 116 | |
| 117 // only process ajax requests | |
| 118 if (!$rcmail->output->ajax_call) { | |
| 119 return; | |
| 120 } | |
| 121 | |
| 122 $this->add_texts('localization'); | |
| 123 | |
| 124 $storage = $rcmail->get_storage(); | |
| 125 $delimiter = $storage->get_hierarchy_delimiter(); | |
| 126 $read_on_move = (bool) $rcmail->config->get('read_on_archive'); | |
| 127 $archive_type = $rcmail->config->get('archive_type', ''); | |
| 128 $archive_folder = $rcmail->config->get('archive_mbox'); | |
| 129 $archive_prefix = $archive_folder . $delimiter; | |
| 130 $current_mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST); | |
| 131 $search_request = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GPC); | |
| 132 $uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST); | |
| 133 | |
| 134 // count messages before changing anything | |
| 135 if ($_POST['_from'] != 'show') { | |
| 136 $threading = (bool) $storage->get_threading(); | |
| 137 $old_count = $storage->count(null, $threading ? 'THREADS' : 'ALL'); | |
| 138 $old_pages = ceil($old_count / $storage->get_pagesize()); | |
| 139 } | |
| 140 | |
| 141 $count = 0; | |
| 142 | |
| 143 // this way response handler for 'move' action will be executed | |
| 144 $rcmail->action = 'move'; | |
| 145 $this->result = array( | |
| 146 'reload' => false, | |
| 147 'error' => false, | |
| 148 'sources' => array(), | |
| 149 'destinations' => array(), | |
| 150 ); | |
| 151 | |
| 152 foreach (rcmail::get_uids(null, null, $multifolder) as $mbox => $uids) { | |
| 153 if (!$archive_folder || strpos($mbox, $archive_prefix) === 0) { | |
| 154 $count = count($uids); | |
| 155 continue; | |
| 156 } | |
| 157 else if (!$archive_type || $archive_type == 'folder') { | |
| 158 $folder = $archive_folder; | |
| 159 | |
| 160 if ($archive_type == 'folder') { | |
| 161 // compose full folder path | |
| 162 $folder .= $delimiter . $mbox; | |
| 163 | |
| 164 // create archive subfolder if it doesn't yet exist | |
| 165 $this->subfolder_worker($folder); | |
| 166 } | |
| 167 | |
| 168 $count += $this->move_messages_worker($uids, $mbox, $folder, $read_on_move); | |
| 169 } | |
| 170 else { | |
| 171 if ($uids == '*') { | |
| 172 $index = $storage->index(null, rcmail_sort_column(), rcmail_sort_order()); | |
| 173 $uids = $index->get(); | |
| 174 } | |
| 175 | |
| 176 $messages = $storage->fetch_headers($mbox, $uids); | |
| 177 $execute = array(); | |
| 178 | |
| 179 foreach ($messages as $message) { | |
| 180 $subfolder = null; | |
| 181 switch ($archive_type) { | |
| 182 case 'year': | |
| 183 $subfolder = $rcmail->format_date($message->timestamp, 'Y'); | |
| 184 break; | |
| 185 | |
| 186 case 'month': | |
| 187 $subfolder = $rcmail->format_date($message->timestamp, 'Y') | |
| 188 . $delimiter . $rcmail->format_date($message->timestamp, 'm'); | |
| 189 break; | |
| 190 | |
| 191 case 'sender': | |
| 192 $from = $message->get('from'); | |
| 193 preg_match('/[\b<](.+@.+)[\b>]/i', $from, $m); | |
| 194 $subfolder = $m[1] ?: $this->gettext('unkownsender'); | |
| 195 | |
| 196 // replace reserved characters in folder name | |
| 197 $repl = $delimiter == '-' ? '_' : '-'; | |
| 198 $replacements[$delimiter] = $repl; | |
| 199 $replacements['.'] = $repl; // some IMAP server do not allow . characters | |
| 200 $subfolder = strtr($subfolder, $replacements); | |
| 201 break; | |
| 202 } | |
| 203 | |
| 204 // compose full folder path | |
| 205 $folder = $archive_folder . ($subfolder ? $delimiter . $subfolder : ''); | |
| 206 | |
| 207 $execute[$folder][] = $message->uid; | |
| 208 } | |
| 209 | |
| 210 foreach ($execute as $folder => $uids) { | |
| 211 // create archive subfolder if it doesn't yet exist | |
| 212 $this->subfolder_worker($folder); | |
| 213 | |
| 214 $count += $this->move_messages_worker($uids, $mbox, $folder, $read_on_move); | |
| 215 } | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 if ($this->result['error']) { | |
| 220 if ($_POST['_from'] != 'show') { | |
| 221 $rcmail->output->command('list_mailbox'); | |
| 222 } | |
| 223 | |
| 224 $rcmail->output->show_message($this->gettext('archiveerror'), 'warning'); | |
| 225 $rcmail->output->send(); | |
| 226 } | |
| 227 | |
| 228 if (!empty($_POST['_refresh'])) { | |
| 229 // FIXME: send updated message rows instead of reloading the entire list | |
| 230 $rcmail->output->command('refresh_list'); | |
| 231 } | |
| 232 else { | |
| 233 $addrows = true; | |
| 234 } | |
| 235 | |
| 236 // refresh saved search set after moving some messages | |
| 237 if ($search_request && $rcmail->storage->get_search_set()) { | |
| 238 $_SESSION['search'] = $rcmail->storage->refresh_search(); | |
| 239 } | |
| 240 | |
| 241 if ($_POST['_from'] == 'show') { | |
| 242 if ($next = rcube_utils::get_input_value('_next_uid', rcube_utils::INPUT_GPC)) { | |
| 243 $rcmail->output->command('show_message', $next); | |
| 244 } | |
| 245 else { | |
| 246 $rcmail->output->command('command', 'list'); | |
| 247 } | |
| 248 | |
| 249 $rcmail->output->send(); | |
| 250 } | |
| 251 | |
| 252 $mbox = $storage->get_folder(); | |
| 253 $msg_count = $storage->count(null, $threading ? 'THREADS' : 'ALL'); | |
| 254 $exists = $storage->count($mbox, 'EXISTS', true); | |
| 255 $page_size = $storage->get_pagesize(); | |
| 256 $page = $storage->get_page(); | |
| 257 $pages = ceil($msg_count / $page_size); | |
| 258 $nextpage_count = $old_count - $page_size * $page; | |
| 259 $remaining = $msg_count - $page_size * ($page - 1); | |
| 260 | |
| 261 // jump back one page (user removed the whole last page) | |
| 262 if ($page > 1 && $remaining == 0) { | |
| 263 $page -= 1; | |
| 264 $storage->set_page($page); | |
| 265 $_SESSION['page'] = $page; | |
| 266 $jump_back = true; | |
| 267 } | |
| 268 | |
| 269 // update message count display | |
| 270 $rcmail->output->set_env('messagecount', $msg_count); | |
| 271 $rcmail->output->set_env('current_page', $page); | |
| 272 $rcmail->output->set_env('pagecount', $pages); | |
| 273 $rcmail->output->set_env('exists', $exists); | |
| 274 | |
| 275 // update mailboxlist | |
| 276 $unseen_count = $msg_count ? $storage->count($mbox, 'UNSEEN') : 0; | |
| 277 $old_unseen = rcmail_get_unseen_count($mbox); | |
| 278 $quota_root = $multifolder ? $this->result['sources'][0] : 'INBOX'; | |
| 279 | |
| 280 if ($old_unseen != $unseen_count) { | |
| 281 $rcmail->output->command('set_unread_count', $mbox, $unseen_count, ($mbox == 'INBOX')); | |
| 282 rcmail_set_unseen_count($mbox, $unseen_count); | |
| 283 } | |
| 284 | |
| 285 $rcmail->output->command('set_quota', $rcmail->quota_content(null, $quota_root)); | |
| 286 $rcmail->output->command('set_rowcount', rcmail_get_messagecount_text($msg_count), $mbox); | |
| 287 | |
| 288 if ($threading) { | |
| 289 $count = rcube_utils::get_input_value('_count', rcube_utils::INPUT_POST); | |
| 290 } | |
| 291 | |
| 292 // add new rows from next page (if any) | |
| 293 if ($addrows && $count && $uids != '*' && ($jump_back || $nextpage_count > 0)) { | |
| 294 // #5862: Don't add more rows than it was on the next page | |
| 295 $count = $jump_back ? null : min($nextpage_count, $count); | |
| 296 | |
| 297 $a_headers = $storage->list_messages($mbox, null, | |
| 298 rcmail_sort_column(), rcmail_sort_order(), $count); | |
| 299 | |
| 300 rcmail_js_message_list($a_headers, false); | |
| 301 } | |
| 302 | |
| 303 if ($this->result['reload']) { | |
| 304 $rcmail->output->show_message($this->gettext('archivedreload'), 'confirmation'); | |
| 305 } | |
| 306 else { | |
| 307 $rcmail->output->show_message($this->gettext('archived'), 'confirmation'); | |
| 308 | |
| 309 if (!$read_on_move) { | |
| 310 foreach ($this->result['destinations'] as $folder) { | |
| 311 rcmail_send_unread_count($folder, true); | |
| 312 } | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 // send response | |
| 317 $rcmail->output->send(); | |
| 318 } | |
| 319 | |
| 320 /** | |
| 321 * Move messages from one folder to another and mark as read if needed | |
| 322 */ | |
| 323 private function move_messages_worker($uids, $from_mbox, $to_mbox, $read_on_move) | |
| 324 { | |
| 325 $storage = rcmail::get_instance()->get_storage(); | |
| 326 | |
| 327 if ($read_on_move) { | |
| 328 // don't flush cache (4th argument) | |
| 329 $storage->set_flag($uids, 'SEEN', $from_mbox, true); | |
| 330 } | |
| 331 | |
| 332 // move message to target folder | |
| 333 if ($storage->move_message($uids, $to_mbox, $from_mbox)) { | |
| 334 if (!in_array($from_mbox, $this->result['sources'])) { | |
| 335 $this->result['sources'][] = $from_mbox; | |
| 336 } | |
| 337 if (!in_array($to_mbox, $this->result['destinations'])) { | |
| 338 $this->result['destinations'][] = $to_mbox; | |
| 339 } | |
| 340 | |
| 341 return count($uids); | |
| 342 } | |
| 343 | |
| 344 $this->result['error'] = true; | |
| 345 } | |
| 346 | |
| 347 /** | |
| 348 * Create archive subfolder if it doesn't yet exist | |
| 349 */ | |
| 350 private function subfolder_worker($folder) | |
| 351 { | |
| 352 $storage = rcmail::get_instance()->get_storage(); | |
| 353 $delimiter = $storage->get_hierarchy_delimiter(); | |
| 354 | |
| 355 if ($this->folders === null) { | |
| 356 $this->folders = $storage->list_folders('', $archive_folder . '*', 'mail', null, true); | |
| 357 } | |
| 358 | |
| 359 if (!in_array($folder, $this->folders)) { | |
| 360 $path = explode($delimiter, $folder); | |
| 361 | |
| 362 // we'll create all folders in the path | |
| 363 for ($i=0; $i<count($path); $i++) { | |
| 364 $_folder = implode($delimiter, array_slice($path, 0, $i+1)); | |
| 365 if (!in_array($_folder, $this->folders)) { | |
| 366 if ($storage->create_folder($_folder, true)) { | |
| 367 $this->result['reload'] = true; | |
| 368 $this->folders[] = $_folder; | |
| 369 } | |
| 370 } | |
| 371 } | |
| 372 } | |
| 373 } | |
| 374 | |
| 375 /** | |
| 376 * Hook to inject plugin-specific user settings | |
| 377 */ | |
| 378 function prefs_table($args) | |
| 379 { | |
| 380 global $CURR_SECTION; | |
| 381 | |
| 382 $this->add_texts('localization'); | |
| 383 | |
| 384 $rcmail = rcmail::get_instance(); | |
| 385 $dont_override = $rcmail->config->get('dont_override', array()); | |
| 386 | |
| 387 if ($args['section'] == 'folders' && !in_array('archive_mbox', $dont_override)) { | |
| 388 $mbox = $rcmail->config->get('archive_mbox'); | |
| 389 $type = $rcmail->config->get('archive_type'); | |
| 390 | |
| 391 // load folders list when needed | |
| 392 if ($CURR_SECTION) { | |
| 393 $select = $rcmail->folder_selector(array( | |
| 394 'noselection' => '---', | |
| 395 'realnames' => true, | |
| 396 'maxlength' => 30, | |
| 397 'folder_filter' => 'mail', | |
| 398 'folder_rights' => 'w', | |
| 399 'onchange' => "if ($(this).val() == 'INBOX') $(this).val('')", | |
| 400 )); | |
| 401 } | |
| 402 else { | |
| 403 $select = new html_select(); | |
| 404 } | |
| 405 | |
| 406 $args['blocks']['main']['options']['archive_mbox'] = array( | |
| 407 'title' => $this->gettext('archivefolder'), | |
| 408 'content' => $select->show($mbox, array('name' => "_archive_mbox")) | |
| 409 ); | |
| 410 | |
| 411 // add option for structuring the archive folder | |
| 412 $archive_type = new html_select(array('name' => '_archive_type', 'id' => 'ff_archive_type')); | |
| 413 $archive_type->add($this->gettext('none'), ''); | |
| 414 $archive_type->add($this->gettext('archivetypeyear'), 'year'); | |
| 415 $archive_type->add($this->gettext('archivetypemonth'), 'month'); | |
| 416 $archive_type->add($this->gettext('archivetypesender'), 'sender'); | |
| 417 $archive_type->add($this->gettext('archivetypefolder'), 'folder'); | |
| 418 | |
| 419 $args['blocks']['archive'] = array( | |
| 420 'name' => rcube::Q($this->gettext('settingstitle')), | |
| 421 'options' => array('archive_type' => array( | |
| 422 'title' => $this->gettext('archivetype'), | |
| 423 'content' => $archive_type->show($type) | |
| 424 ) | |
| 425 ) | |
| 426 ); | |
| 427 } | |
| 428 else if ($args['section'] == 'server' && !in_array('read_on_archive', $dont_override)) { | |
| 429 $chbox = new html_checkbox(array('name' => '_read_on_archive', 'id' => 'ff_read_on_archive', 'value' => 1)); | |
| 430 $args['blocks']['main']['options']['read_on_archive'] = array( | |
| 431 'title' => $this->gettext('readonarchive'), | |
| 432 'content' => $chbox->show($rcmail->config->get('read_on_archive') ? 1 : 0) | |
| 433 ); | |
| 434 } | |
| 435 | |
| 436 return $args; | |
| 437 } | |
| 438 | |
| 439 /** | |
| 440 * Hook to save plugin-specific user settings | |
| 441 */ | |
| 442 function save_prefs($args) | |
| 443 { | |
| 444 $rcmail = rcmail::get_instance(); | |
| 445 $dont_override = $rcmail->config->get('dont_override', array()); | |
| 446 | |
| 447 if ($args['section'] == 'folders' && !in_array('archive_mbox', $dont_override)) { | |
| 448 $args['prefs']['archive_type'] = rcube_utils::get_input_value('_archive_type', rcube_utils::INPUT_POST); | |
| 449 } | |
| 450 else if ($args['section'] == 'server' && !in_array('read_on_archive', $dont_override)) { | |
| 451 $args['prefs']['read_on_archive'] = (bool) rcube_utils::get_input_value('_read_on_archive', rcube_utils::INPUT_POST); | |
| 452 } | |
| 453 | |
| 454 return $args; | |
| 455 } | |
| 456 } |
