From 4fe67ca69790ddeb9b85be15e2b7a5e9e451ce03 Mon Sep 17 00:00:00 2001 From: pdontthink Date: Thu, 22 May 2008 01:19:00 +0000 Subject: [PATCH] Add address list pagination, 'Compose To' button, more labels for checkboxes, hook points for plugins to modify abook nav bar and filter address listings git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@13160 7612ce4b-ef26-0410-bec9-ea0150e637f0 --- ChangeLog | 5 + functions/addressbook.php | 5 +- functions/template/abook_util.php | 268 +++++++++++++++++++- include/load_prefs.php | 6 + include/options/display.php | 39 +++ src/addressbook.php | 123 +++++++-- templates/default/addressbook_list.tpl | 140 +++++----- templates/default/addressbook_paginator.tpl | 67 +++++ templates/default/css/default.css | 14 +- 9 files changed, 575 insertions(+), 92 deletions(-) create mode 100644 templates/default/addressbook_paginator.tpl diff --git a/ChangeLog b/ChangeLog index 9246d801..a9e9f431 100644 --- a/ChangeLog +++ b/ChangeLog @@ -248,6 +248,11 @@ Version 1.5.2 - SVN default_pref file next to hardcoding them into the DB class, thanks Thierry Godefroy. - Reimplement printer friendly to make use of CSS. + - Enhanced address book page: added address list pagination, added + 'Compose to' button, put labels around address entries tied to + checkboxes, added hook and template plugin output sections for + plugins that can filter address book listings and modify the abook + navigation bar. Complements RisuMail team (risumail.jp). Version 1.5.1 (branched on 2006-02-12) -------------------------------------- diff --git a/functions/addressbook.php b/functions/addressbook.php index 36edcb02..a802c03a 100644 --- a/functions/addressbook.php +++ b/functions/addressbook.php @@ -363,9 +363,8 @@ function show_abook_sort_button($abook_sort_order, $alt_tag, $which = 8; } - $uri = $form_url .'?abook_sort_order=' . $which; - foreach ($uri_extra as $key => $value) - $uri = set_url_var($uri, $key, $value, FALSE); + $uri_extra['abook_sort_order'] = $which; + $uri = set_uri_vars($form_url, $uri_extra, FALSE); /* Now that we have everything figured out, show the actual button. */ return create_hyperlink($uri, diff --git a/functions/template/abook_util.php b/functions/template/abook_util.php index 53bfb400..55d55c16 100644 --- a/functions/template/abook_util.php +++ b/functions/template/abook_util.php @@ -14,16 +14,17 @@ /** - * Display a column header with sort buttons - * - * @param string $field Which field to display - * @param int $backend The abook backend to be shown when - * sort link is clicked - * - * @author Steve Brown - * @since 1.5.2 - */ -function addAbookSort ($field, $backend) { + * Display a column header with sort buttons + * + * @param string $field Which field to display + * @param array $current_page_args All known query string arguments + * for the current page request; structured + * as an associative array of key/value pairs + * + * @author Steve Brown + * @since 1.5.2 + */ +function addAbookSort ($field, $current_page_args) { global $abook_sort_order, $nbsp; switch ($field) { @@ -60,7 +61,252 @@ function addAbookSort ($field, $backend) { } // show_abook_sort_button() creates a hyperlink (using hyperlink.tpl) that encompases an image, using a getImage() call - return $str . ($has_sort ? $nbsp . show_abook_sort_button($abook_sort_order, $alt, $down, $up, array('new_bnum' => $backend)) : ''); + return $str . ($has_sort ? $nbsp . show_abook_sort_button($abook_sort_order, $alt, $down, $up, $current_page_args) : ''); +} + + +/** + * Creates an address book paginator + * + * @param boolean $abook_page_selector Whether or not to show the page selector + * @param int $abook_page_selector_max The maximum number of page links to show + * on screen + * @param int $page_number What page is being viewed - 0 if not used + * @param int $page_size Maximum number of addresses to be shown + * per page + * @param int $total_addresses The total count of addresses in the backend + * @param boolean $show_all Whether or not all addresses are being shown + * @param array $current_page_args All known query string arguments + * for the current page request; structured + * as an associative array of key/value pairs + * @param boolean $compact Whether or not to build a smaller, + * "compact" paginator + * + * @return string The paginator, ready for output + * + */ +function get_abook_paginator($abook_page_selector, $abook_page_selector_max, + $page_number, $page_size, $total_addresses, + $show_all, $current_page_args, $compact) { + + // if showing all, just show pagination link + // + if ($show_all) + { + unset($current_page_args['show_all']); + return '[' . make_abook_paginator_link(1, _("Paginate"), $current_page_args) . ']'; + } + + + // if we don't have enough information to build the paginator, return nothing + // + if (empty($page_number) || empty($page_size) || empty($total_addresses)) + return ''; + + + // calculate some values we need below + // + $show_elipses_before = FALSE; + $show_elipses_after = FALSE; + global $nbsp; + $sep = '|'; + $paginator_string = '['; + $total_pages = ceil($total_addresses / $page_size); + if ($page_number > $total_pages) $page_number = $total_pages; + $spacing = ($compact ? $nbsp : $nbsp . $nbsp); + + + // only enough addresses for one page anyway? no pagination needed + // + if ($total_pages < 2) return ''; + + + // build "Show All" link + // + $show_all_string = '[' + . make_abook_paginator_link(1, _("Show All"), + array_merge($current_page_args, array('show_all' => 1))) + . ']'; + + + // build next/previous links for compact paginator + // + if ($compact) + { + if ($page_number > 1) + $paginator_string .= make_abook_paginator_link(1, + _("<<"), + $current_page_args) + . '][' + . make_abook_paginator_link($page_number - 1, + _("<"), + $current_page_args) + . ']['; + else + $paginator_string .= _("<<") . '][' . _("<") . ']['; + if ($page_number < $total_pages) + $paginator_string .= make_abook_paginator_link($page_number + 1, + _(">"), + $current_page_args) + . '][' + . make_abook_paginator_link($total_pages, + _(">>"), + $current_page_args) + . ']'; + else + $paginator_string .= _(">") . '][' . _(">>") . ']'; + } + + + // build next/previous links for regular paginator + // + else + { + if ($page_number > 1) + $paginator_string .= make_abook_paginator_link($page_number - 1, + _("Previous"), + $current_page_args); + else + $paginator_string .= _("Previous"); + $paginator_string .= $nbsp . $sep . $nbsp; + if ($page_number < $total_pages) + $paginator_string .= make_abook_paginator_link($page_number + 1, + _("Next"), + $current_page_args); + else + $paginator_string .= _("Next"); + $paginator_string .= ']'; + } + + + // paginator is turned off - just show previous/next links + // + if (!$abook_page_selector) + { + return $paginator_string . $spacing . $show_all_string; + } + + + $paginator_string .= $spacing; + + + if ($total_pages <= $abook_page_selector_max) + { + $start_page = 1; + $end_page = $total_pages; + } + else + { + $pages_to_show = ($abook_page_selector_max % 2 ? $abook_page_selector_max : $abook_page_selector_max - 1); + $end_page = $page_number + floor($pages_to_show / 2); + $start_page = $page_number - floor($pages_to_show / 2); + if (!($abook_page_selector_max % 2)) $start_page--; + + if ($start_page < 1) + { + $end_page += 1 - $start_page; + $start_page = 1; + } + else if ($end_page > $total_pages) + { + $start_page -= $end_page - $total_pages; + $end_page = $total_pages; + } + + + // do we need to insert elipses? + // + if (1 < $start_page) + { + $start_page++; + $show_elipses_before = TRUE; + } + if ($total_pages > $end_page) + { + $end_page--; + $show_elipses_after = TRUE; + } + } + + + // now build the actual (compact) paginator + // + if ($compact) + { + $aValues = array(); + for ($i = 1; $i <= $total_pages; $i++) + $aValues[$i] = $i . '/' . $total_pages; + $page_uri = sqm_baseuri() . 'src/addressbook.php'; + $temp_page_number = $current_page_args['page_number']; + unset($current_page_args['page_number']); + $page_uri = set_uri_vars($page_uri, array_diff($current_page_args, array('page_number' => 0)), FALSE); + $current_page_args['page_number'] = $temp_page_number; + $paginator_string .= addSelect('page_number', $aValues, + $page_number, TRUE, + (checkForJavascript() + ? array('onchange' => 'SubmitOnSelect(this, \'' + . $page_uri + . '&page_number=' + . '\')') + : array())); + + // need a submit button when select widget cannot submit itself + // + if (!checkForJavascript()) + { + $paginator_string .= addSubmit(_("Go"), 'paginator_submit'); + } + } + + + // now build the actual (regular) paginator + // + else + { + $paginator_string .= '[' . $nbsp; + if ($show_elipses_before) + $paginator_string .= make_abook_paginator_link(1, 1, $current_page_args) + . $nbsp . '...' . $nbsp; + for ($x = $start_page; $x <= $end_page; $x++) + { + if ($x == $page_number) + $paginator_string .= $x . $nbsp; + else + $paginator_string .= make_abook_paginator_link($x, $x, $current_page_args) . $nbsp; + } + if ($show_elipses_after) + $paginator_string .= '...' . $nbsp + . make_abook_paginator_link($total_pages, $total_pages, $current_page_args) + . $nbsp; + $paginator_string .= ']'; + } + $paginator_string .= $spacing . $show_all_string; + + + return $paginator_string; + +} + + +/** + * Build a page (pagination) link for use with the address book list page + * + * @param int $page_number The page number for the link + * @param string $text The link text + * @param array $current_page_args All known query string arguments + * for the current page request; structured + * as an associative array of key/value pairs + * + */ +function make_abook_paginator_link($page_number, $text, $current_page_args) { + + $uri = sqm_baseuri() . 'src/addressbook.php'; + + $current_page_args['page_number'] = $page_number; + $uri = set_uri_vars($uri, $current_page_args, FALSE); + + return create_hyperlink($uri, $text); + } diff --git a/include/load_prefs.php b/include/load_prefs.php index c3d5f56d..65fc2734 100644 --- a/include/load_prefs.php +++ b/include/load_prefs.php @@ -278,6 +278,12 @@ $page_selector = getPref($data_dir, $username, 'page_selector', SMPREF_ON); $compact_paginator = getPref($data_dir, $username, 'compact_paginator', SMPREF_OFF); $page_selector_max = getPref($data_dir, $username, 'page_selector_max', 10); +/* Abook page selector options */ +$abook_show_num = getPref($data_dir, $username, 'abook_show_num', 15 ); +$abook_page_selector = getPref($data_dir, $username, 'abook_page_selector', SMPREF_ON); +$abook_compact_paginator = getPref($data_dir, $username, 'abook_compact_paginator', SMPREF_OFF); +$abook_page_selector_max = getPref($data_dir, $username, 'abook_page_selector_max', 5); + /* SqClock now in the core */ $date_format = getPref($data_dir, $username, 'date_format', 3); $hour_format = getPref($data_dir, $username, 'hour_format', SMPREF_TIME_12HR); diff --git a/include/options/display.php b/include/options/display.php index 4ba39452..ef7d2663 100644 --- a/include/options/display.php +++ b/include/options/display.php @@ -15,6 +15,7 @@ define('SMOPT_GRP_GENERAL', 0); define('SMOPT_GRP_MAILBOX', 1); define('SMOPT_GRP_MESSAGE', 2); +define('SMOPT_GRP_ABOOK', 3); global $use_iframe; if (! isset($use_iframe)) $use_iframe=false; @@ -400,6 +401,44 @@ FIXME! 'refresh' => SMOPT_REFRESH_ALL ); + + + /*** Load the Address Book Options into the array ***/ + $optgrps[SMOPT_GRP_ABOOK] = _("Address Book Display Options"); + $optvals[SMOPT_GRP_ABOOK] = array(); + + $optvals[SMOPT_GRP_ABOOK][] = array( + 'name' => 'abook_show_num', + 'caption' => _("Number of Addresses per Page"), + 'type' => SMOPT_TYPE_INTEGER, + 'refresh' => SMOPT_REFRESH_NONE, + 'size' => SMOPT_SIZE_TINY + ); + + $optvals[SMOPT_GRP_ABOOK][] = array( + 'name' => 'abook_page_selector', + 'caption' => _("Enable Page Selector"), + 'type' => SMOPT_TYPE_BOOLEAN, + 'refresh' => SMOPT_REFRESH_NONE + ); + + $optvals[SMOPT_GRP_ABOOK][] = array( + 'name' => 'abook_compact_paginator', + 'caption' => _("Use Compact Page Selector"), + 'type' => SMOPT_TYPE_BOOLEAN, + 'refresh' => SMOPT_REFRESH_NONE + ); + + $optvals[SMOPT_GRP_ABOOK][] = array( + 'name' => 'abook_page_selector_max', + 'caption' => _("Maximum Number of Pages to Show"), + 'type' => SMOPT_TYPE_INTEGER, + 'refresh' => SMOPT_REFRESH_NONE, + 'size' => SMOPT_SIZE_TINY + ); + + + /* Assemble all this together and return it as our result. */ $result = array( 'grps' => $optgrps, diff --git a/src/addressbook.php b/src/addressbook.php index 6daf3fd7..214a6f8f 100644 --- a/src/addressbook.php +++ b/src/addressbook.php @@ -39,6 +39,12 @@ sqgetGlobalVar('sel', $sel, SQ_POST); sqgetGlobalVar('oldnick', $oldnick, SQ_POST); sqgetGlobalVar('backend', $backend, SQ_POST); sqgetGlobalVar('doedit', $doedit, SQ_POST); +$page_size = $abook_show_num; +if (!sqGetGlobalVar('page_number', $page_number, SQ_FORM)) + if (!sqGetGlobalVar('current_page_number', $page_number, SQ_FORM)) + $page_number = 1; +if (!sqGetGlobalVar('show_all', $show_all, SQ_FORM)) + $show_all = 0; /* Get sorting order */ $abook_sort_order = get_abook_sort(); @@ -51,7 +57,7 @@ displayPageHeader($color); */ $abook = addressbook_init(true, false); -// FIXME: do we have to stop use of address book, when localbackend is not present. +// FIXME: do we really have to stop use of address book when localbackend is not present? if($abook->localbackend == 0) { plain_error_message(_("No personal address book is defined. Contact administrator.")); exit(); @@ -268,6 +274,7 @@ if(sqgetGlobalVar('REQUEST_METHOD', $req_method, SQ_SERVER) && $req_method == 'P // Some times we end output before forms are printed if($abortform) { +//FIXME: use footer.tpl; remove HTML from core echo "\n"; exit(); } @@ -294,38 +301,120 @@ while (list($k, $backend) = each ($abook->backends)) { $a['BackendWritable'] = $backend->writeable; $a['Addresses'] = array(); - $alist = $abook->list_addr($backend->bnum); + // don't do address lookup if we are not viewing that backend + // + if ($backend->bnum == $current_backend) { + $alist = $abook->list_addr($backend->bnum); - /* check return (array with data or boolean false) */ - if (is_array($alist)) { - usort($alist,'alistcmp'); - - $a['Addresses'] = formatAddressList($alist); + /* check return (array with data or boolean false) */ + if (is_array($alist)) { + usort($alist,'alistcmp'); + + $a['Addresses'] = formatAddressList($alist); - $addresses[$backend->bnum] = $a; + $addresses[$backend->bnum] = $a; + } else { + // list_addr() returns boolean + plain_error_message(nl2br(htmlspecialchars($abook->error))); + } } else { - // list_addr() returns boolean - plain_error_message(nl2br(htmlspecialchars($abook->error))); + $addresses[$backend->bnum] = $a; + } +} + + +$current_page_args = array( + 'abook_sort_order' => $abook_sort_order, + 'new_bnum' => $current_backend, + 'page_number' => $page_number, + ); + + +// note that plugins can add to $current_page_args as well as +// filter the address list +// +$temp = array(&$addresses, &$current_backend, &$page_number, &$current_page_args); +do_hook('abook_list_filter', $temp); + + +// NOTE to address book backend authors and plugin authors: if a backend does +// pagination (which might be more efficient), it needs to place a key +// in every address listing it returns called "paginated", whose value +// should evaluate to boolean TRUE. However, if a plugin will also be +// used on the hook above to filter the addresses (perhaps by group), then +// the backend should be made compatible with the filtering plugin and +// should do the actual filtering too. Otherwise, the backend will paginate +// before filtering has taken place, the output of which is clearly wrong. +// It is proposed that filtering be based on a GET/POST variable called +// "abook_groups_X" where X is the current backend number. The value of +// this varaible would be an array of possible filter names, which the +// plugin and the backend would both know about. The plugin would only +// filter based on that value if the backend didn't already do it. The +// backend can insert a "grouped" key into all address listings, whose +// value evaluates to boolean TRUE, telling the plugin not to do any +// filtering itself. For an example of this implementation, see the +// Address Book Grouping and Pagination plugin. + + +// if no pagination was done by a plugin or the abook +// backend (which is indicated by the presence of a +// "paginated" key within all of the address entries +// in the list of addresses for the backend currently +// being viewed), then we provide default pagination +// +$total_addresses = 0; +if (!$show_all + && is_array($addresses[$current_backend]['Addresses']) + && empty($addresses[$current_backend]['Addresses'][0]['paginated'])) { + + // at this point, we assume the current list is + // the *full* list + // + $total_addresses = sizeof($addresses[$current_backend]['Addresses']); + + // iterate through all the entries, building list of addresses + // to keep based on current page + // + $new_address_list = array(); + $total_pages = ceil($total_addresses / $page_size); + if ($page_number > $total_pages) $page_number = $total_pages; + $page_count = 1; + $page_item_count = 0; + foreach ($addresses[$current_backend]['Addresses'] as $addr) { + $page_item_count++; + if ($page_item_count > $page_size) { + $page_count++; + $page_item_count = 1; + } + if ($page_count == $page_number) + $new_address_list[] = $addr; } + $addresses[$current_backend]['Addresses'] = $new_address_list; + } if ($showaddrlist) { -//FIXME: Remove HTML from here! - echo addForm($form_url, 'post', 'address_book_form'); - $oTemplate->assign('compose_new_win', $compose_new_win); - $oTemplate->assign('compose_height', $compose_height); - $oTemplate->assign('compose_width', $compose_width); + $oTemplate->assign('show_all', $show_all); + $oTemplate->assign('page_number', $page_number); + $oTemplate->assign('page_size', $page_size); + $oTemplate->assign('total_addresses', $total_addresses); + $oTemplate->assign('abook_compact_paginator', $abook_compact_paginator); + $oTemplate->assign('abook_page_selector', $abook_page_selector); + $oTemplate->assign('current_page_args', $current_page_args); + $oTemplate->assign('abook_page_selector_max', $abook_page_selector_max); $oTemplate->assign('addresses', $addresses); $oTemplate->assign('current_backend', $current_backend); $oTemplate->assign('backends', $list_backends); $oTemplate->assign('abook_has_extra_field', $abook->add_extra_field); + $oTemplate->assign('compose_new_win', $compose_new_win); + $oTemplate->assign('compose_height', $compose_height); + $oTemplate->assign('compose_width', $compose_width); + $oTemplate->assign('form_action', $form_url); $oTemplate->display('addressbook_list.tpl'); -//FIXME: Remove HTML from here! - echo "\n"; } /* Display the "new address" form */ diff --git a/templates/default/addressbook_list.tpl b/templates/default/addressbook_list.tpl index 1da30ad6..121d7537 100644 --- a/templates/default/addressbook_list.tpl +++ b/templates/default/addressbook_list.tpl @@ -1,50 +1,58 @@ +
@@ -64,13 +73,21 @@ $colspan = $abook_has_extra_field ? 6 : 5; - - - - - + + + + '; @@ -107,17 +124,21 @@ $colspan = $abook_has_extra_field ? 6 : 5; echo ''."\n"; } foreach ($source['Addresses'] as $contact) { - $id = $current_backend . '_' . $contact['NickName']; - ?> - + echo ''; + if (!empty($contact['special_message'])) { + echo ''; + } else { + $id = $current_backend . '_' . $contact['NickName']; + ?> '.$contact['Extra'].''."\n"; + if ($abook_has_extra_field) { + echo ''."\n"; + } } ?> @@ -127,3 +148,4 @@ $colspan = $abook_has_extra_field ? 6 : 5; ?>
- " name="editaddr" id="editaddr" /> - " name="deladdr" id="deladdr" /> - name="compose_to" id="compose_to" /> - + +
+ display('addressbook_paginator.tpl'); + if (!empty($plugin_output['address_book_navigation'])) echo $plugin_output['address_book_navigation']; + ?> +
+ " name="editaddr" id="editaddr" /> + " name="deladdr" id="deladdr" /> + name="compose_to" id="compose_to" /> + +
+ 0) { ?> @@ -91,10 +108,10 @@ $colspan = $abook_has_extra_field ? 6 : 5;
" onclick="toggle_all('address_book_form', 'sel', false); return false;" />
'._("Address book is empty").'
' . $contact['special_message'] . '' : ''); ?> '.$contact['Extra'].'
+
diff --git a/templates/default/addressbook_paginator.tpl b/templates/default/addressbook_paginator.tpl new file mode 100644 index 00000000..c1c167f2 --- /dev/null +++ b/templates/default/addressbook_paginator.tpl @@ -0,0 +1,67 @@ + + + + + + + tag just to buy needed space on crowded nav bar -- should we remove and find another solution for un-crowding the nav bar? + echo '' . get_abook_paginator($abook_page_selector, $abook_page_selector_max, $page_number, $page_size, $total_addresses, $show_all, $current_page_args, $abook_compact_paginator) . ''; + diff --git a/templates/default/css/default.css b/templates/default/css/default.css index 1a89b89d..249e5a05 100644 --- a/templates/default/css/default.css +++ b/templates/default/css/default.css @@ -529,13 +529,23 @@ td.message_count { text-align: center; background: #ababab /* __COLOR9__ */; } -#addressList td.abookSwitch { + +#addressList select { + vertical-align: middle; +} + +#addressList td.abookPaginationAndButtons { background: #dcdcdc /* __COLOR0__ */; text-align: right; } -#addressList td.abookButtons { +#addressList div.abookPagination { + float: left; +} + +#addressList td.abookSwitch { background: #dcdcdc /* __COLOR0__ */; + text-align: right; } #addressList td.abookField { -- 2.25.1