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 } |