1<?php 2 3/** 4 * Core modules 5 * @package modules 6 * @subpackage core 7 */ 8 9/** 10 * get basic message list settings 11 * @subpackage core/functions 12 * @param string $path message list path 13 * @param object $handler hm handler module 14 * @return array 15 */ 16if (!hm_exists('get_message_list_settings')) { 17function get_message_list_settings($path, $handler) { 18 $list_path = $path; 19 $mailbox_list_title = array(); 20 $message_list_since = DEFAULT_SINCE; 21 $per_source_limit = DEFAULT_PER_SOURCE; 22 23 if ($path == 'unread') { 24 $list_path = 'unread'; 25 $mailbox_list_title = array('Unread'); 26 $message_list_since = $handler->user_config->get('unread_since_setting', DEFAULT_SINCE); 27 $per_source_limit = $handler->user_config->get('unread_per_source_setting', DEFAULT_PER_SOURCE); 28 } 29 elseif ($path == 'email') { 30 $message_list_since = $handler->user_config->get('all_email_since_setting', DEFAULT_SINCE); 31 $per_source_limit = $handler->user_config->get('all_email_per_source_setting', DEFAULT_PER_SOURCE); 32 $list_path = 'email'; 33 $mailbox_list_title = array('All Email'); 34 } 35 elseif ($path == 'flagged') { 36 $list_path = 'flagged'; 37 $message_list_since = $handler->user_config->get('flagged_since_setting', DEFAULT_SINCE); 38 $per_source_limit = $handler->user_config->get('flagged_per_source_setting', DEFAULT_PER_SOURCE); 39 $mailbox_list_title = array('Flagged'); 40 } 41 elseif ($path == 'combined_inbox') { 42 $list_path = 'combined_inbox'; 43 $message_list_since = $handler->user_config->get('all_since_setting', DEFAULT_SINCE); 44 $per_source_limit = $handler->user_config->get('all_per_source_setting', DEFAULT_PER_SOURCE); 45 $mailbox_list_title = array('Everything'); 46 } 47 return array($list_path, $mailbox_list_title, $message_list_since, $per_source_limit); 48}} 49 50/** 51 * Build meta information for a message list 52 * @subpackage core/functions 53 * @param array $input module output 54 * @param object $output_mod Hm_Output_Module 55 * @return string 56 */ 57if (!hm_exists('message_list_meta')) { 58function message_list_meta($input, $output_mod) { 59 if (!array_key_exists('list_meta', $input) || !$input['list_meta']) { 60 return ''; 61 } 62 $limit = 0; 63 $since = false; 64 $times = array( 65 'today' => 'Today', 66 '-1 week' => 'Last 7 days', 67 '-2 weeks' => 'Last 2 weeks', 68 '-4 weeks' => 'Last 4 weeks', 69 '-6 weeks' => 'Last 6 weeks', 70 '-6 months' => 'Last 6 months', 71 '-1 year' => 'Last year', 72 '-5 years' => 'Last 5 years' 73 ); 74 if (array_key_exists('per_source_limit', $input)) { 75 $limit = $input['per_source_limit']; 76 } 77 if (!$limit) { 78 $limit = DEFAULT_PER_SOURCE; 79 } 80 if (array_key_exists('message_list_since', $input)) { 81 $since = $input['message_list_since']; 82 } 83 if (!$since) { 84 $since = DEFAULT_SINCE; 85 } 86 $date = sprintf('%s', strtolower($output_mod->trans($times[$since]))); 87 $max = sprintf($output_mod->trans('sources@%d each'), $limit); 88 89 return '<div class="list_meta">'. 90 $date. 91 '<b>-</b>'. 92 '<span class="src_count"></span> '.$max. 93 '<b>-</b>'. 94 '<span class="total"></span> '.$output_mod->trans('total').'</div>'; 95}} 96 97/** 98 * Build sort dialog for a combined list 99 * @subpackage core/functions 100 * @param object $output_mod Hm_Output_Module 101 * @return string 102 */ 103if (!hm_exists('combined_sort_dialog')) { 104function combined_sort_dialog($mod) { 105 $sorts = array( 106 '4' => $mod->trans('Arrival'), 107 '2' => $mod->trans('From'), 108 '3' => $mod->trans('Subject'), 109 ); 110 111 $res = '<select name="sort" class="combined_sort">'; 112 foreach ($sorts as $name => $val) { 113 $res .= '<option value="'.$name.'">'.$val.' ↓</option>'; 114 $res .= '<option value="-'.$name.'">'.$val.' ↑</option>'; 115 } 116 $res .= '</select>'; 117 return $res; 118}} 119 120/** 121 * Build a human readable interval string 122 * @subpackage core/functions 123 * @param string $date_str date string parsable by strtotime() 124 * @return string 125 */ 126if (!hm_exists('human_readable_interval')) { 127function human_readable_interval($date_str) { 128 $precision = 2; 129 $interval_time = array(); 130 $date = strtotime($date_str); 131 $interval = time() - $date; 132 $res = array(); 133 134 $t['second'] = 1; 135 $t['minute'] = $t['second']*60; 136 $t['hour'] = $t['minute']*60; 137 $t['day'] = $t['hour']*24; 138 $t['week'] = $t['day']*7; 139 $t['month'] = $t['day']*30; 140 $t['year'] = $t['week']*52; 141 142 if ($interval < 0) { 143 return 'From the future!'; 144 } 145 elseif ($interval == 0) { 146 return 'Just now'; 147 } 148 foreach (array_reverse($t) as $name => $val) { 149 if ($interval_time[$name] = ($interval/$val > 0) ? floor($interval/$val) : false) { 150 $interval -= $val*$interval_time[$name]; 151 } 152 } 153 $interval_time = array_slice(array_filter($interval_time, function($v) { return $v > 0; }), 0, $precision); 154 foreach($interval_time as $name => $val) { 155 if ($val > 1) { 156 $res[] = sprintf('%d %ss', $val, $name); 157 } 158 else { 159 $res[] = sprintf('%d %s', $val, $name); 160 } 161 } 162 return implode(', ', $res); 163}} 164 165/** 166 * Output a message list row of data using callbacks 167 * @subpackage core/functions 168 * @param array $values data and callbacks for each cell 169 * @param string $id unique id for the message 170 * @param string $style message list style (news or email) 171 * @param object $output_mod Hm_Output_Module 172 * @param string $row_class optional table row css class 173 * @return array 174 */ 175if (!hm_exists('message_list_row')) { 176function message_list_row($values, $id, $style, $output_mod, $row_class='') { 177 $res = '<tr class="'.$output_mod->html_safe($id); 178 if ($row_class) { 179 $res .= ' '.$output_mod->html_safe($row_class); 180 } 181 $res .= '">'; 182 if ($style == 'news') { 183 $res .= '<td class="news_cell checkbox_cell">'; 184 } 185 foreach ($values as $vals) { 186 if (function_exists($vals[0])) { 187 $function = array_shift($vals); 188 $res .= $function($vals, $style, $output_mod); 189 } 190 } 191 if ($style == 'news') { 192 $res .= '</td>'; 193 } 194 $res .= '</tr>'; 195 return array($res, $id); 196}} 197 198/** 199 * Generic callback for a message list table cell 200 * @subpackage core/functions 201 * @param array $vals data for the cell 202 * @param string $style message list style 203 * @param object $output_mod Hm_Output_Module 204 * @return string 205 */ 206if (!hm_exists('safe_output_callback')) { 207function safe_output_callback($vals, $style, $output_mod) { 208 $img = ''; 209 if (count($vals) == 3 && $vals[2]) { 210 $img = '<img src="'.Hm_Image_Sources::${$vals[2]}.'" />'; 211 } 212 if ($style == 'news') { 213 return sprintf('<div class="%s" title="%s">%s%s</div>', $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1]), $img, $output_mod->html_safe($vals[1])); 214 } 215 return sprintf('<td class="%s" title="%s">%s%s</td>', $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1]), $img, $output_mod->html_safe($vals[1])); 216}} 217 218/** 219 * Callback for a message list checkbox 220 * @subpackage core/functions 221 * @param array $vals data for the cell 222 * @param string $style message list style 223 * @param object $output_mod Hm_Output_Module 224 * @return string 225 */ 226if (!hm_exists('checkbox_callback')) { 227function checkbox_callback($vals, $style, $output_mod) { 228 if ($style == 'news') { 229 return sprintf('<input type="checkbox" id="%s" value="%s" />'. 230 '<label class="checkbox_label" for="%s"></label>'. 231 '</td><td class="news_cell">', $output_mod->html_safe($vals[0]), 232 $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[0])); 233 } 234 return sprintf('<td class="checkbox_cell">'. 235 '<input id="'.$output_mod->html_safe($vals[0]).'" type="checkbox" value="%s" />'. 236 '<label class="checkbox_label" for="'.$output_mod->html_safe($vals[0]).'"></label>'. 237 '</td>', $output_mod->html_safe($vals[0])); 238}} 239 240/** 241 * Callback for a subject cell in a message list 242 * @subpackage core/functions 243 * @param array $vals data for the cell 244 * @param string $style message list style 245 * @param object $output_mod Hm_Output_Module 246 * @return string 247 */ 248if (!hm_exists('subject_callback')) { 249function subject_callback($vals, $style, $output_mod) { 250 $img = ''; 251 if (count($vals) == 4 && $vals[3]) { 252 $img = '<img alt="'.$output_mod->trans('list item').'" src="'.Hm_Image_Sources::${$vals[3]}.'" />'; 253 } 254 $subject = $output_mod->html_safe($vals[0]); 255 $hl_subject = preg_replace("/^(\[[^\]]+\])/", '<span class="s_pre">$1</span>', $subject); 256 if ($style == 'news') { 257 return sprintf('<div class="subject"><div class="%s" title="%s">%s <a href="%s">%s</a></div></div>', $output_mod->html_safe(implode(' ', $vals[2])), $subject, $img, $output_mod->html_safe($vals[1]), $hl_subject); 258 } 259 return sprintf('<td class="subject"><div class="%s"><a title="%s" href="%s">%s</a></div></td>', $output_mod->html_safe(implode(' ', $vals[2])), $subject, $output_mod->html_safe($vals[1]), $hl_subject); 260}} 261 262/** 263 * Callback for a date cell in a message list 264 * @subpackage core/functions 265 * @param array $vals data for the cell 266 * @param string $style message list style 267 * @param object $output_mod Hm_Output_Module 268 * @return string 269 */ 270if (!hm_exists('date_callback')) { 271function date_callback($vals, $style, $output_mod) { 272 if ($style == 'news') { 273 return sprintf('<div class="msg_date">%s<input type="hidden" class="msg_timestamp" value="%s" /></div>', $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); 274 } 275 return sprintf('<td class="msg_date" title="%s">%s<input type="hidden" class="msg_timestamp" value="%s" /></td>', $output_mod->html_safe(date('r', $vals[1])), $output_mod->html_safe($vals[0]), $output_mod->html_safe($vals[1])); 276}} 277 278/** 279 * Callback for an icon in a message list row 280 * @subpackage core/functions 281 * @param array $vals data for the cell 282 * @param string $style message list style 283 * @param object $output_mod Hm_Output_Module 284 * @return string 285 */ 286if (!hm_exists('icon_callback')) { 287function icon_callback($vals, $style, $output_mod) { 288 $icons = ''; 289 $title = array(); 290 $show_icons = $output_mod->get('msg_list_icons'); 291 if (in_array('flagged', $vals[0])) { 292 $icons .= $show_icons ? '<img src="'.Hm_Image_Sources::$star.'" width="16" height="16" alt="'.$output_mod->trans('Flagged').'" />' : ' F'; 293 $title[] = $output_mod->trans('Flagged'); 294 } 295 if (in_array('answered', $vals[0])) { 296 $icons .= $show_icons ? '<img src="'.Hm_Image_Sources::$circle_check.'" width="16" height="16" alt="'.$output_mod->trans('Answered').'" />' : ' A'; 297 $title[] = $output_mod->trans('Answered'); 298 } 299 if (in_array('attachment', $vals[0])) { 300 $icons .= $show_icons ? '<img src="'.Hm_Image_Sources::$paperclip.'" width="16" height="16" alt="'.$output_mod->trans('Attachment').'" />' : ' +'; 301 $title[] = $output_mod->trans('Attachment'); 302 } 303 $title = implode(', ', $title); 304 if ($style == 'news') { 305 return sprintf('<div class="icon" title="%s">%s</div>', $title, $icons); 306 } 307 return sprintf('<td class="icon" title="%s">%s</td>', $title, $icons); 308}} 309 310/** 311 * Output message controls 312 * @subpackage core/functions 313 * @param object $output_mod Hm_Output_Module 314 * @return string 315 */ 316if (!hm_exists('message_controls')) { 317function message_controls($output_mod) { 318 $res = '<a class="toggle_link" href="#"><img alt="x" src="'.Hm_Image_Sources::$check.'" width="8" height="8" /></a>'. 319 '<div class="msg_controls">'. 320 '<a class="msg_read core_msg_control" href="#" data-action="read">'.$output_mod->trans('Read').'</a>'. 321 '<a class="msg_unread core_msg_control" href="#" data-action="unread">'.$output_mod->trans('Unread').'</a>'. 322 '<a class="msg_flag core_msg_control" href="#" data-action="flag">'.$output_mod->trans('Flag').'</a>'. 323 '<a class="msg_unflag core_msg_control" href="#" data-action="unflag">'.$output_mod->trans('Unflag').'</a>'. 324 '<a class="msg_delete core_msg_control" href="#" data-action="delete">'.$output_mod->trans('Delete').'</a>'; 325 if ($output_mod->get('msg_controls_extra')) { 326 $res .= $output_mod->get('msg_controls_extra'); 327 } 328 $res .= '</div>'; 329 return $res; 330}} 331 332/** 333 * Output select element for "received since" options 334 * @subpackage core/functions 335 * @param string $since current value to pre-select 336 * @param string $name name used as the elements id and name 337 * @param object $output_mod Hm_Output_Module 338 * @return string 339 */ 340if (!hm_exists('message_since_dropdown')) { 341function message_since_dropdown($since, $name, $output_mod) { 342 $times = array( 343 'today' => 'Today', 344 '-1 week' => 'Last 7 days', 345 '-2 weeks' => 'Last 2 weeks', 346 '-4 weeks' => 'Last 4 weeks', 347 '-6 weeks' => 'Last 6 weeks', 348 '-6 months' => 'Last 6 months', 349 '-1 year' => 'Last year', 350 '-5 years' => 'Last 5 years' 351 ); 352 $res = '<select name="'.$name.'" id="'.$name.'" class="message_list_since">'; 353 foreach ($times as $val => $label) { 354 $res .= '<option'; 355 if ($val == $since) { 356 $res .= ' selected="selected"'; 357 } 358 $res .= ' value="'.$val.'">'.$output_mod->trans($label).'</option>'; 359 } 360 $res .= '</select>'; 361 return $res; 362}} 363 364/** 365 * Output a source list for a message list 366 * @subpackage core/functions 367 * @param array $sources source of the list 368 * @param object $output_mod Hm_Output_Module 369 */ 370if (!hm_exists('list_sources')) { 371function list_sources($sources, $output_mod) { 372 $res = '<div class="list_sources">'; 373 $res .= '<div class="src_title">'.$output_mod->html_safe('Sources').'</div>'; 374 foreach ($sources as $src) { 375 if (array_key_exists('group', $src) && $src['group'] == 'background') { 376 continue; 377 } 378 if (array_key_exists('nodisplay', $src) && $src['nodisplay']) { 379 continue; 380 } 381 if ($src['type'] == 'imap' && !array_key_exists('folder', $src)) { 382 $folder = '_INBOX'; 383 } 384 elseif (!array_key_exists('folder', $src)) { 385 $folder = ''; 386 } 387 else { 388 $folder = '_'.hex2bin($src['folder']); 389 } 390 $res .= '<div class="list_src">'.$output_mod->html_safe($src['type']).' '.$output_mod->html_safe($src['name']); 391 $res .= ' '.$output_mod->html_safe(str_replace('_', '', $folder)); 392 $res .= '</div>'; 393 } 394 $res .= '</div>'; 395 return $res; 396}} 397 398/** 399 * Output message list controls 400 * @subpackage core/functions 401 * @param string $refresh_link refresh link tag 402 * @param string $config_link configuration link tag 403 * @param string $source_link source link tag 404 * @return string 405 */ 406if (!hm_exists('list_controls')) { 407function list_controls($refresh_link, $config_link, $source_link=false) { 408 return '<div class="list_controls">'. 409 $refresh_link.$source_link.$config_link.'</div>'; 410}} 411 412/** 413 * Validate search terms 414 * @subpackage core/functions 415 * @param string $terms search terms to validate 416 * @return string 417 */ 418if (!hm_exists('validate_search_terms')) { 419function validate_search_terms($terms) { 420 $terms = trim(strip_tags($terms)); 421 if (!$terms) { 422 $terms = ''; 423 } 424 return $terms; 425}} 426 427/** 428 * Validate the name of a search field 429 * @subpackage core/functions 430 * @param string $fld name to validate 431 * @return mixed 432 */ 433if (!hm_exists('validate_search_fld')) { 434function validate_search_fld($fld) { 435 if (in_array($fld, array('TEXT', 'BODY', 'FROM', 'SUBJECT'))) { 436 return $fld; 437 } 438 return false; 439}} 440 441/** 442 * Output a select element for the search field 443 * @subpackage core/functions 444 * @param string $current currently selected field 445 * @param object $output_mod Hm_Output_Module 446 * @return string 447 */ 448if (!hm_exists('search_field_selection')) { 449function search_field_selection($current, $output_mod) { 450 $flds = array( 451 'TEXT' => 'Entire message', 452 'BODY' => 'Message body', 453 'SUBJECT' => 'Subject', 454 'FROM' => 'From', 455 ); 456 $res = '<select id="search_fld" name="search_fld">'; 457 foreach ($flds as $val => $name) { 458 $res .= '<option '; 459 if ($current == $val) { 460 $res .= 'selected="selected" '; 461 } 462 $res .= 'value="'.$val.'">'.$output_mod->trans($name).'</option>'; 463 } 464 $res .= '</select>'; 465 return $res; 466}} 467 468/** 469 * Build pagination links for a list of messages 470 * @subpackage core/functions 471 * @param int $page_size number of messages per page 472 * @param int $current current page number 473 * @param int $total number of messages total 474 * @param string $path list path 475 * @param string $filter list filter 476 * @param string $sort list sort 477 * @return string 478 */ 479if (!hm_exists('build_page_links')) { 480function build_page_links($page_size, $current_page, $total, $path, $filter=false, $sort=false) { 481 $links = ''; 482 $first = ''; 483 $last = ''; 484 $display_links = 10; 485 if ($filter) { 486 $filter_str = '&filter='.$filter; 487 } 488 else { 489 $filter_str = ''; 490 } 491 if ($sort) { 492 $sort_str = '&sort='.$sort; 493 } 494 else { 495 $sort_str = ''; 496 } 497 498 $max_pages = ceil($total/$page_size); 499 if ($max_pages == 1) { 500 return ''; 501 } 502 $floor = ($current_page - 1) - intval($display_links/2); 503 if ($floor < 0) { 504 $floor = 1; 505 } 506 $ceil = $floor + $display_links; 507 if ($ceil > $max_pages) { 508 $floor -= ($ceil - $max_pages); 509 } 510 $prev = '<a class="disabled_link"><img src="'.Hm_Image_Sources::$caret_left.'" alt="←" /></a>'; 511 $next = '<a class="disabled_link"><img src="'.Hm_Image_Sources::$caret_right.'" alt="→" /></a>'; 512 513 if ($floor > 1 ) { 514 $first = '<a href="?page=message_list&list_path='.urlencode($path).'&list_page=1'.$filter_str.$sort_str.'">1</a> ... '; 515 } 516 if ($ceil < $max_pages) { 517 $last = ' ... <a href="?page=message_list&list_path='.urlencode($path).'&list_page='.$max_pages.$filter_str.$sort_str.'">'.$max_pages.'</a>'; 518 } 519 if ($current_page > 1) { 520 $prev = '<a href="?page=message_list&list_path='.urlencode($path).'&list_page='.($current_page - 1).$filter_str.$sort_str.'"><img src="'.Hm_Image_Sources::$caret_left.'" alt="←" /></a>'; 521 } 522 if ($max_pages > 1 && $current_page < $max_pages) { 523 $next = '<a href="?page=message_list&list_path='.urlencode($path).'&list_page='.($current_page + 1).$filter_str.$sort_str.'"><img src="'.Hm_Image_Sources::$caret_right.'" alt="→" /></a>'; 524 } 525 for ($i=1;$i<=$max_pages;$i++) { 526 if ($i < $floor || $i > $ceil) { 527 continue; 528 } 529 $links .= ' <a '; 530 if ($i == $current_page) { 531 $links .= 'class="current_page" '; 532 } 533 $links .= 'href="?page=message_list&list_path='.urlencode($path).'&list_page='.$i.$filter_str.$sort_str.'">'.$i.'</a>'; 534 } 535 return $prev.' '.$first.$links.$last.' '.$next; 536}} 537 538