/**
 * Builds pagination links for HTML display.
 *
 * The pagination is quite configurable, but at the same time gives a consistent 
 * look and feel to all pagination.
 *
 * This function takes one array that contains the options to configure the 
 * pagination. Required options include:
 *
 * - url: The base URL to use for all links
 * - count: The total number of results to paginate for
 * - limit: How many to show per page
 * - offset: At which result to start showing results
 *
 * Optional options include:
 *
 * - id: The ID of the div enclosing the pagination
 * - class: The class of the div enclosing the pagination
 * - offsetname: The name of the offset parameter in the url
 * - firsttext: The text to use for the 'first page' link
 * - previoustext: The text to use for the 'previous page' link
 * - nexttext: The text to use for the 'next page' link
 * - lasttext: The text to use for the 'last page' link
 * - numbersincludefirstlast: Whether the page numbering should include links 
 *   for the first and last pages
 * - resultcounttextsingular: The text to use for 'result'
 * - resultcounttextplural: The text to use for 'results'
 *
 * Optional options to support javascript pagination include:
 *
 * - datatable: The ID of the table whose TBODY's rows will be replaced with the 
 *   resulting rows
 * - jsonscript: The script to make a json request to in order to retrieve 
 *   both the new rows and the new pagination. See js/artefactchooser.json.php 
 *   for an example. Note that the paginator javascript library is NOT 
 *   automatically included just because you call this function, so make sure 
 *   that your smarty() call hooks it in.
 *
 * @param array $params Options for the pagination
 */
function build_pagination($params)
{
    // Bail if the required attributes are not present
    $required = array('url', 'count', 'limit', 'offset');
    foreach ($required as $option) {
        if (!isset($params[$option])) {
            throw new ParameterException('You must supply option "' . $option . '" to build_pagination');
        }
    }
    // Work out default values for parameters
    if (!isset($params['id'])) {
        $params['id'] = substr(md5(microtime()), 0, 4);
    }
    $params['offsetname'] = isset($params['offsetname']) ? $params['offsetname'] : 'offset';
    if (isset($params['forceoffset']) && !is_null($params['forceoffset'])) {
        $params['offset'] = (int) $params['forceoffset'];
    } else {
        if (!isset($params['offset'])) {
            $params['offset'] = param_integer($params['offsetname'], 0);
        }
    }
    // Correct for odd offsets
    $params['offset'] -= $params['offset'] % $params['limit'];
    $params['firsttext'] = isset($params['firsttext']) ? $params['firsttext'] : get_string('first');
    $params['previoustext'] = isset($params['previoustext']) ? $params['previoustext'] : get_string('previous');
    $params['nexttext'] = isset($params['nexttext']) ? $params['nexttext'] : get_string('next');
    $params['lasttext'] = isset($params['lasttext']) ? $params['lasttext'] : get_string('last');
    $params['resultcounttextsingular'] = isset($params['resultcounttextsingular']) ? $params['resultcounttextsingular'] : get_string('result');
    $params['resultcounttextplural'] = isset($params['resultcounttextplural']) ? $params['resultcounttextplural'] : get_string('results');
    if (!isset($params['numbersincludefirstlast'])) {
        $params['numbersincludefirstlast'] = true;
    }
    if (!isset($params['numbersincludeprevnext'])) {
        $params['numbersincludeprevnext'] = true;
    }
    if (!isset($params['extradata'])) {
        $params['extradata'] = null;
    }
    // Begin building the output
    $output = '<div id="' . $params['id'] . '" class="pagination';
    if (isset($params['class'])) {
        $output .= ' ' . hsc($params['class']);
    }
    $output .= '">';
    if ($params['limit'] <= $params['count']) {
        $pages = ceil($params['count'] / $params['limit']);
        $page = $params['offset'] / $params['limit'];
        $last = $pages - 1;
        if (!empty($params['lastpage'])) {
            $page = $last;
        }
        $next = min($last, $page + 1);
        $prev = max(0, $page - 1);
        // Build a list of what pagenumbers will be put between the previous/next links
        $pagenumbers = array();
        if ($params['numbersincludefirstlast']) {
            $pagenumbers[] = 0;
        }
        if ($params['numbersincludeprevnext']) {
            $pagenumbers[] = $prev;
        }
        $pagenumbers[] = $page;
        if ($params['numbersincludeprevnext']) {
            $pagenumbers[] = $next;
        }
        if ($params['numbersincludefirstlast']) {
            $pagenumbers[] = $last;
        }
        $pagenumbers = array_unique($pagenumbers);
        // Build the first/previous links
        $isfirst = $page == 0;
        $output .= build_pagination_pagelink('first', $params['url'], 0, '&laquo; ' . $params['firsttext'], get_string('firstpage'), $isfirst, $params['offsetname']);
        $output .= build_pagination_pagelink('prev', $params['url'], $params['limit'] * $prev, '&larr; ' . $params['previoustext'], get_string('prevpage'), $isfirst, $params['offsetname']);
        // Build the pagenumbers in the middle
        foreach ($pagenumbers as $k => $i) {
            if ($k != 0 && $prevpagenum < $i - 1) {
                $output .= '…';
            }
            if ($i == $page) {
                $output .= '<span class="selected">' . ($i + 1) . '</span>';
            } else {
                $output .= build_pagination_pagelink('', $params['url'], $params['limit'] * $i, $i + 1, '', false, $params['offsetname']);
            }
            $prevpagenum = $i;
        }
        // Build the next/last links
        $islast = $page == $last;
        $output .= build_pagination_pagelink('next', $params['url'], $params['limit'] * $next, $params['nexttext'] . ' &rarr;', get_string('nextpage'), $islast, $params['offsetname']);
        $output .= build_pagination_pagelink('last', $params['url'], $params['limit'] * $last, $params['lasttext'] . ' &raquo;', get_string('lastpage'), $islast, $params['offsetname']);
    }
    // Work out what javascript we need for the paginator
    $js = '';
    if (isset($params['jsonscript']) && isset($params['datatable'])) {
        $paginator_js = hsc(get_config('wwwroot') . 'js/paginator.js');
        $id = json_encode($params['id']);
        $datatable = json_encode($params['datatable']);
        $jsonscript = json_encode($params['jsonscript']);
        $extradata = json_encode($params['extradata']);
        $js .= "new Paginator({$id}, {$datatable}, {$jsonscript}, {$extradata});";
    }
    // Output the count of results
    $resultsstr = $params['count'] == 1 ? $params['resultcounttextsingular'] : $params['resultcounttextplural'];
    $output .= '<div class="results">' . $params['count'] . ' ' . $resultsstr . '</div>';
    // Close the container div
    $output .= '</div>';
    return array('html' => $output, 'javascript' => $js);
}
Exemple #2
0
/**
* Builds pagination links for HTML display.
*
* The pagination is quite configurable, but at the same time gives a consistent
* look and feel to all pagination.
*
* This function takes one array that contains the options to configure the
* pagination. Required options include:
*
* - url: The base URL to use for all links (it should not contain special characters)
* - count: The total number of results to paginate for
* - setlimit: toggle variable for enabling/disabling limit dropbox, default value = false
* - limit: How many to show per page
* - offset: At which result to start showing results
*
* Optional options include:
*
* - id: The ID of the div enclosing the pagination
* - class: The class of the div enclosing the pagination
* - offsetname: The name of the offset parameter in the url
* - firsttext: The text to use for the 'first page' link
* - previoustext: The text to use for the 'previous page' link
* - nexttext: The text to use for the 'next page' link
* - lasttext: The text to use for the 'last page' link
* - numbersincludefirstlast: Whether the page numbering should include links
*   for the first and last pages
* - numbersincludeprevnext: The number of pagelinks, adjacent the the current page,
*   to include per side
* - jumplinks: The maximum number of page jump links to have between first- and current-,
    and current- and last page
* - resultcounttextsingular: The text to use for 'result'
* - resultcounttextplural: The text to use for 'results'
* - limittext: The text to use for the limitoption, e.g. "Max items per page" or "Page size"
*
* Optional options to support javascript pagination include:
*
* - datatable: The ID of the table whose TBODY's rows will be replaced with the
*   resulting rows
* - jsonscript: The script to make a json request to in order to retrieve
*   both the new rows and the new pagination. See js/artefactchooser.json.php
*   for an example. Note that the paginator javascript library is NOT
*   automatically included just because you call this function, so make sure
*   that your smarty() call hooks it in.
*
* @param array $params Options for the pagination
*/
function build_pagination($params)
{
    $limitoptions = array(1, 10, 20, 50, 100, 500);
    // Bail if the required attributes are not present
    $required = array('url', 'count', 'limit', 'offset');
    foreach ($required as $option) {
        if (!isset($params[$option])) {
            throw new ParameterException('You must supply option "' . $option . '" to build_pagination');
        }
    }
    if (isset($params['setlimit']) && $params['setlimit']) {
        if (!in_array($params['limit'], $limitoptions)) {
            $params['limit'] = 10;
        }
        if (!isset($params['limittext'])) {
            $params['limittext'] = get_string('maxitemsperpage1');
        }
    } else {
        $params['setlimit'] = false;
    }
    // Work out default values for parameters
    if (!isset($params['id'])) {
        $params['id'] = substr(md5(microtime()), 0, 4);
    }
    $params['offsetname'] = isset($params['offsetname']) ? $params['offsetname'] : 'offset';
    if (isset($params['forceoffset']) && !is_null($params['forceoffset'])) {
        $params['offset'] = (int) $params['forceoffset'];
    } else {
        if (!isset($params['offset'])) {
            $params['offset'] = param_integer($params['offsetname'], 0);
        }
    }
    // Correct for odd offsets
    if ($params['limit']) {
        $params['offset'] -= $params['offset'] % $params['limit'];
    }
    $params['firsttext'] = isset($params['firsttext']) ? $params['firsttext'] : get_string('first');
    $params['previoustext'] = isset($params['previoustext']) ? $params['previoustext'] : get_string('previous');
    $params['nexttext'] = isset($params['nexttext']) ? $params['nexttext'] : get_string('next');
    $params['lasttext'] = isset($params['lasttext']) ? $params['lasttext'] : get_string('last');
    $params['resultcounttextsingular'] = isset($params['resultcounttextsingular']) ? $params['resultcounttextsingular'] : get_string('result');
    $params['resultcounttextplural'] = isset($params['resultcounttextplural']) ? $params['resultcounttextplural'] : get_string('results');
    if (!isset($params['numbersincludefirstlast'])) {
        $params['numbersincludefirstlast'] = true;
    }
    if (!isset($params['numbersincludeprevnext'])) {
        $params['numbersincludeprevnext'] = 1;
    } else {
        $params['numbersincludeprevnext'] = (int) $params['numbersincludeprevnext'];
    }
    if (!isset($params['extradata'])) {
        $params['extradata'] = null;
    }
    // Begin building the output
    $output = '<div id="' . $params['id'] . '" class="pagination-wrapper';
    if (isset($params['class'])) {
        $output .= ' ' . hsc($params['class']);
    }
    $output .= '">';
    // Output the count of results
    $resultsstr = $params['count'] == 1 ? $params['resultcounttextsingular'] : $params['resultcounttextplural'];
    if ($params['count'] > 0) {
        $output .= '<div class="lead text-small results pull-right">' . $params['count'] . ' ' . $resultsstr . '</div>';
    }
    $output .= '<ul class="pagination pagination-xs">';
    if ($params['limit'] && $params['limit'] < $params['count']) {
        $pages = ceil($params['count'] / $params['limit']);
        $page = $params['offset'] / $params['limit'];
        $last = $pages - 1;
        if (!empty($params['lastpage'])) {
            $page = $last;
        }
        $prev = max(0, $page - 1);
        $next = min($last, $page + 1);
        // Build a list of what pagenumbers will be put between the previous/next links
        $pagenumbers = array();
        // First page
        if ($params['numbersincludefirstlast']) {
            $pagenumbers[] = 0;
        }
        $maxjumplinks = isset($params['jumplinks']) ? (int) $params['jumplinks'] : 0;
        // Jump pages between first page and current page
        $betweencount = $page;
        $jumplinks = $pages ? round($maxjumplinks * ($betweencount / $pages)) : 0;
        $jumpcount = $jumplinks ? round($betweencount / ($jumplinks + 1)) : 0;
        $gapcount = 1;
        if ($jumpcount > 1) {
            for ($bc = 1; $bc < $betweencount; $bc++) {
                if ($gapcount > $jumpcount) {
                    $pagenumbers[] = $bc;
                    $gapcount = 1;
                }
                $gapcount++;
            }
        }
        // Current page with adjacent prev and next pages
        if ($params['numbersincludeprevnext'] > 0) {
            for ($i = 1; $i <= $params['numbersincludeprevnext']; $i++) {
                $prevlink = $page - $i;
                if ($prevlink < 0) {
                    break;
                }
                $pagenumbers[] = $prevlink;
            }
            unset($prevlink);
        }
        $pagenumbers[] = $page;
        if ($params['numbersincludeprevnext'] > 0) {
            for ($i = 1; $i <= $params['numbersincludeprevnext']; $i++) {
                $nextlink = $page + $i;
                if ($nextlink > $last) {
                    break;
                }
                $pagenumbers[] = $nextlink;
            }
        }
        // Jump pages between current and last
        $betweencount = $pages - $page;
        $jumplinks = $pages ? round($maxjumplinks * ($betweencount / $pages)) : 0;
        $jumpcount = $jumplinks ? round($betweencount / ($jumplinks + 1)) : 0;
        $gapcount = 1;
        if ($jumpcount > 1) {
            for ($bc = $page; $bc < $last; $bc++) {
                if ($gapcount > $jumpcount) {
                    $pagenumbers[] = $bc;
                    $gapcount = 1;
                }
                $gapcount++;
            }
        }
        // Last page
        if ($params['numbersincludefirstlast']) {
            $pagenumbers[] = $last;
        }
        $pagenumbers = array_unique($pagenumbers);
        sort($pagenumbers);
        // Build the first/previous links
        $isfirst = $page == 0;
        $output .= build_pagination_pagelink('', '&laquo;', get_string('prevpage'), $isfirst, $params['url'], $params['setlimit'], $params['limit'], $params['limit'] * $prev, $params['offsetname']);
        // Build the pagenumbers in the middle
        foreach ($pagenumbers as $k => $i) {
            // add ellipsis if pages skipped
            $text = $i + 1;
            if ($k != 0 && $prevpagenum < $i - 1) {
                $text = $i + 1 . '<span class="metadata hidden-xs">...</span>';
            }
            if ($i == $page) {
                $output .= build_pagination_pagelink('active', $text, '', true);
            } else {
                $output .= build_pagination_pagelink('', $text, '', false, $params['url'], $params['setlimit'], $params['limit'], $params['limit'] * $i, $params['offsetname']);
            }
            $prevpagenum = $i;
        }
        // Build the next/last links
        $islast = $page == $last;
        $output .= build_pagination_pagelink('', ' &raquo;', get_string('nextpage'), $islast, $params['url'], $params['setlimit'], $params['limit'], $params['limit'] * $next, $params['offsetname']);
    }
    // Build limitoptions dropbox if results are more than 10 (minimum dropbox pagination)
    if ($params['setlimit'] && $params['count'] > 10) {
        $strlimitoptions = array();
        $limit = $params['limit'];
        for ($i = 0; $i < count($limitoptions); $i++) {
            if ($limit == $limitoptions[$i]) {
                $strlimitoptions[] = "<option value = '{$limit}' selected='selected'> {$limit} </option>";
            } else {
                $strlimitoptions[] = "<option value = '{$limitoptions[$i]}'> {$limitoptions[$i]} </option>";
            }
        }
        $output .= '</ul>';
        $output .= '<form class="form-pagination js-pagination form-inline pagination-page-limit dropdown" action="' . hsc($params['url']) . '" method="POST">
            <label for="setlimitselect" class="set-limit"> ' . $params['limittext'] . ' </label>' . '<span class="picker input-sm"><select id="setlimitselect" class="js-pagination input-sm select form-control" name="limit"> ' . join(' ', $strlimitoptions) . '</select></span>
            <input class="currentoffset" type="hidden" name="' . $params['offsetname'] . '" value="' . $params['offset'] . '"/>
            <input class="pagination js-hidden hidden" type="submit" name="submit" value="' . get_string('change') . '"/>
        </form>';
    } else {
        if ($params['setlimit']) {
            $output .= '<input type="hidden" id="setlimitselect" name="limit" value="' . $params['limit'] . '">';
        }
    }
    // Work out what javascript we need for the paginator
    $js = '';
    $id = json_encode($params['id']);
    if (isset($params['jsonscript']) && isset($params['datatable'])) {
        $paginator_js = hsc(get_config('wwwroot') . 'js/paginator.js');
        $datatable = json_encode($params['datatable']);
        if (!empty($params['searchresultsheading'])) {
            $heading = json_encode($params['searchresultsheading']);
        } else {
            $heading = 'null';
        }
        $jsonscript = json_encode($params['jsonscript']);
        $extradata = json_encode($params['extradata']);
        $js .= "new Paginator({$id}, {$datatable}, {$heading}, {$jsonscript}, {$extradata});";
    } else {
        $js .= "new Paginator({$id}, null, null, null, null);";
    }
    // Close the container div
    $output .= '</div>';
    return array('html' => $output, 'javascript' => $js);
}