Subversion Repositories Applications.papyrus

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
848 florian 1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
 
4
/**
5
 * Contains the Pager_Common class
6
 *
7
 * PHP versions 4 and 5
8
 *
9
 * LICENSE: Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. The name of the author may not be used to endorse or promote products
17
 *    derived from this software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
20
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
 * IN NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
23
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 *
30
 * @category   HTML
31
 * @package    Pager
32
 * @author     Lorenzo Alberton <l dot alberton at quipo dot it>
33
 * @author     Richard Heyes <richard@phpguru.org>
34
 * @copyright  2003-2006 Lorenzo Alberton, Richard Heyes
35
 * @license    http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
36
 * @version    CVS: $Id$
37
 * @link       http://pear.php.net/package/Pager
38
 */
39
 
40
/**
41
 * Two constants used to guess the path- and file-name of the page
42
 * when the user doesn't set any other value
43
 */
44
if (substr($_SERVER['PHP_SELF'], -1) == '/') {
45
    define('CURRENT_FILENAME', '');
46
    define('CURRENT_PATHNAME', 'http://'.$_SERVER['HTTP_HOST'].str_replace('\\', '/', $_SERVER['PHP_SELF']));
47
} else {
48
    define('CURRENT_FILENAME', preg_replace('/(.*)\?.*/', '\\1', basename($_SERVER['PHP_SELF'])));
49
    define('CURRENT_PATHNAME', str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])));
50
}
51
/**
52
 * Error codes
53
 */
54
define('PAGER_OK',                         0);
55
define('ERROR_PAGER',                     -1);
56
define('ERROR_PAGER_INVALID',             -2);
57
define('ERROR_PAGER_INVALID_PLACEHOLDER', -3);
58
define('ERROR_PAGER_INVALID_USAGE',       -4);
59
define('ERROR_PAGER_NOT_IMPLEMENTED',     -5);
60
 
61
/**
62
 * Pager_Common - Common base class for [Sliding|Jumping] Window Pager
63
 * Extend this class to write a custom paging class
64
 *
65
 * @category   HTML
66
 * @package    Pager
67
 * @author     Lorenzo Alberton <l dot alberton at quipo dot it>
68
 * @author     Richard Heyes <richard@phpguru.org>
69
 * @copyright  2003-2005 Lorenzo Alberton, Richard Heyes
70
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
71
 * @link       http://pear.php.net/package/Pager
72
 */
73
class Pager_Common
74
{
75
    // {{{ class vars
76
 
77
    /**
78
     * @var integer number of items
79
     * @access private
80
     */
81
    var $_totalItems;
82
 
83
    /**
84
     * @var integer number of items per page
85
     * @access private
86
     */
87
    var $_perPage     = 10;
88
 
89
    /**
90
     * @var integer number of page links for each window
91
     * @access private
92
     */
93
    var $_delta       = 10;
94
 
95
    /**
96
     * @var integer current page number
97
     * @access private
98
     */
99
    var $_currentPage = 1;
100
 
101
    /**
102
     * @var integer total pages number
103
     * @access private
104
     */
105
    var $_totalPages  = 1;
106
 
107
    /**
108
     * @var string CSS class for links
109
     * @access private
110
     */
111
    var $_linkClass   = '';
112
 
113
    /**
114
     * @var string wrapper for CSS class name
115
     * @access private
116
     */
117
    var $_classString = '';
118
 
119
    /**
120
     * @var string path name
121
     * @access private
122
     */
123
    var $_path        = CURRENT_PATHNAME;
124
 
125
    /**
126
     * @var string file name
127
     * @access private
128
     */
129
    var $_fileName    = CURRENT_FILENAME;
130
 
131
    /**
132
     * @var boolean If false, don't override the fileName option. Use at your own risk.
133
     * @access private
134
     */
135
    var $_fixFileName = true;
136
 
137
    /**
138
     * @var boolean you have to use FALSE with mod_rewrite
139
     * @access private
140
     */
141
    var $_append      = true;
142
 
143
    /**
144
     * @var string specifies which HTTP method to use
145
     * @access private
146
     */
147
    var $_httpMethod  = 'GET';
148
 
149
    /**
150
     * @var string specifies which HTML form to use
151
     * @access private
152
     */
153
    var $_formID  = '';
154
 
155
    /**
156
     * @var boolean whether or not to import submitted data
157
     * @access private
158
     */
159
    var $_importQuery = true;
160
 
161
    /**
162
     * @var string name of the querystring var for pageID
163
     * @access private
164
     */
165
    var $_urlVar      = 'pageID';
166
 
167
    /**
168
     * @var array data to pass through the link
169
     * @access private
170
     */
171
    var $_linkData    = array();
172
 
173
    /**
174
     * @var array additional URL vars
175
     * @access private
176
     */
177
    var $_extraVars   = array();
178
 
179
    /**
180
     * @var array URL vars to ignore
181
     * @access private
182
     */
183
    var $_excludeVars = array();
184
 
185
    /**
186
     * @var boolean TRUE => expanded mode (for Pager_Sliding)
187
     * @access private
188
     */
189
    var $_expanded    = true;
190
 
191
    /**
192
     * @var boolean TRUE => show accesskey attribute on <a> tags
193
     * @access private
194
     */
195
    var $_accesskey   = false;
196
 
197
    /**
198
     * @var string extra attributes for the <a> tag
199
     * @access private
200
     */
201
    var $_attributes  = '';
202
 
203
    /**
204
     * @var string alt text for "first page" (use "%d" placeholder for page number)
205
     * @access private
206
     */
207
    var $_altFirst     = 'first page';
208
 
209
    /**
210
     * @var string alt text for "previous page"
211
     * @access private
212
     */
213
    var $_altPrev     = 'previous page';
214
 
215
    /**
216
     * @var string alt text for "next page"
217
     * @access private
218
     */
219
    var $_altNext     = 'next page';
220
 
221
    /**
222
     * @var string alt text for "last page" (use "%d" placeholder for page number)
223
     * @access private
224
     */
225
    var $_altLast     = 'last page';
226
 
227
    /**
228
     * @var string alt text for "page"
229
     * @access private
230
     */
231
    var $_altPage     = 'page';
232
 
233
    /**
234
     * @var string image/text to use as "prev" link
235
     * @access private
236
     */
237
    var $_prevImg     = '&lt;&lt; Back';
238
 
239
    /**
240
     * @var string image/text to use as "next" link
241
     * @access private
242
     */
243
    var $_nextImg     = 'Next &gt;&gt;';
244
 
245
    /**
246
     * @var string link separator
247
     * @access private
248
     */
249
    var $_separator   = '';
250
 
251
    /**
252
     * @var integer number of spaces before separator
253
     * @access private
254
     */
255
    var $_spacesBeforeSeparator = 0;
256
 
257
    /**
258
     * @var integer number of spaces after separator
259
     * @access private
260
     */
261
    var $_spacesAfterSeparator  = 1;
262
 
263
    /**
264
     * @var string CSS class name for current page link
265
     * @access private
266
     */
267
    var $_curPageLinkClassName  = '';
268
 
269
    /**
270
     * @var string Text before current page link
271
     * @access private
272
     */
273
    var $_curPageSpanPre        = '';
274
 
275
    /**
276
     * @var string Text after current page link
277
     * @access private
278
     */
279
    var $_curPageSpanPost       = '';
280
 
281
    /**
282
     * @var string Text before first page link
283
     * @access private
284
     */
285
    var $_firstPagePre  = '[';
286
 
287
    /**
288
     * @var string Text to be used for first page link
289
     * @access private
290
     */
291
    var $_firstPageText = '';
292
 
293
    /**
294
     * @var string Text after first page link
295
     * @access private
296
     */
297
    var $_firstPagePost = ']';
298
 
299
    /**
300
     * @var string Text before last page link
301
     * @access private
302
     */
303
    var $_lastPagePre   = '[';
304
 
305
    /**
306
     * @var string Text to be used for last page link
307
     * @access private
308
     */
309
    var $_lastPageText  = '';
310
 
311
    /**
312
     * @var string Text after last page link
313
     * @access private
314
     */
315
    var $_lastPagePost  = ']';
316
 
317
    /**
318
     * @var string Will contain the HTML code for the spaces
319
     * @access private
320
     */
321
    var $_spacesBefore  = '';
322
 
323
    /**
324
     * @var string Will contain the HTML code for the spaces
325
     * @access private
326
     */
327
    var $_spacesAfter   = '';
328
 
329
    /**
330
     * @var string $_firstLinkTitle
331
     * @access private
332
     */
333
    var $_firstLinkTitle = 'first page';
334
 
335
    /**
336
     * @var string $_nextLinkTitle
337
     * @access private
338
     */
339
    var $_nextLinkTitle = 'next page';
340
 
341
    /**
342
     * @var string $_prevLinkTitle
343
     * @access private
344
     */
345
    var $_prevLinkTitle = 'previous page';
346
 
347
    /**
348
     * @var string $_lastLinkTitle
349
     * @access private
350
     */
351
    var $_lastLinkTitle = 'last page';
352
 
353
    /**
354
     * @var string Text to be used for the 'show all' option in the select box
355
     * @access private
356
     */
357
    var $_showAllText   = '';
358
 
359
    /**
360
     * @var array data to be paged
361
     * @access private
362
     */
363
    var $_itemData      = null;
364
 
365
    /**
366
     * @var boolean If TRUE and there's only one page, links aren't shown
367
     * @access private
368
     */
369
    var $_clearIfVoid   = true;
370
 
371
    /**
372
     * @var boolean Use session for storing the number of items per page
373
     * @access private
374
     */
375
    var $_useSessions   = false;
376
 
377
    /**
378
     * @var boolean Close the session when finished reading/writing data
379
     * @access private
380
     */
381
    var $_closeSession  = false;
382
 
383
    /**
384
     * @var string name of the session var for number of items per page
385
     * @access private
386
     */
387
    var $_sessionVar    = 'setPerPage';
388
 
389
    /**
390
     * Pear error mode (when raiseError is called)
391
     * (see PEAR doc)
392
     *
393
     * @var int $_pearErrorMode
394
     * @access private
395
     */
396
    var $_pearErrorMode = null;
397
 
398
    // }}}
399
    // {{{ public vars
400
 
401
    /**
402
     * @var string Complete set of links
403
     * @access public
404
     */
405
    var $links = '';
406
 
407
    /**
408
     * @var string Complete set of link tags
409
     * @access public
410
     */
411
    var $linkTags = '';
412
 
413
    /**
414
     * @var array Array with a key => value pair representing
415
     *            page# => bool value (true if key==currentPageNumber).
416
     *            can be used for extreme customization.
417
     * @access public
418
     */
419
    var $range = array();
420
 
421
    /**
422
     * @var array list of available options (safety check)
423
     * @access private
424
     */
425
    var $_allowed_options = array(
426
        'totalItems',
427
        'perPage',
428
        'delta',
429
        'linkClass',
430
        'path',
431
        'fileName',
432
        'fixFileName',
433
        'append',
434
        'httpMethod',
435
        'formID',
436
        'importQuery',
437
        'urlVar',
438
        'altFirst',
439
        'altPrev',
440
        'altNext',
441
        'altLast',
442
        'altPage',
443
        'prevImg',
444
        'nextImg',
445
        'expanded',
446
        'accesskey',
447
        'attributes',
448
        'separator',
449
        'spacesBeforeSeparator',
450
        'spacesAfterSeparator',
451
        'curPageLinkClassName',
452
        'curPageSpanPre',
453
        'curPageSpanPost',
454
        'firstPagePre',
455
        'firstPageText',
456
        'firstPagePost',
457
        'lastPagePre',
458
        'lastPageText',
459
        'lastPagePost',
460
        'firstLinkTitle',
461
        'nextLinkTitle',
462
        'prevLinkTitle',
463
        'lastLinkTitle',
464
        'showAllText',
465
        'itemData',
466
        'clearIfVoid',
467
        'useSessions',
468
        'closeSession',
469
        'sessionVar',
470
        'pearErrorMode',
471
        'extraVars',
472
        'excludeVars',
473
        'currentPage',
474
    );
475
 
476
    // }}}
477
    // {{{ build()
478
 
479
    /**
480
     * Generate or refresh the links and paged data after a call to setOptions()
481
     *
482
     * @access public
483
     */
484
    function build()
485
    {
486
        $msg = '<b>PEAR::Pager Error:</b>'
487
              .' function "build()" not implemented.';
488
        return $this->raiseError($msg, ERROR_PAGER_NOT_IMPLEMENTED);
489
    }
490
 
491
    // }}}
492
    // {{{ getPageData()
493
 
494
    /**
495
     * Returns an array of current pages data
496
     *
497
     * @param $pageID Desired page ID (optional)
498
     * @return array Page data
499
     * @access public
500
     */
501
    function getPageData($pageID = null)
502
    {
503
        $pageID = empty($pageID) ? $this->_currentPage : $pageID;
504
 
505
        if (!isset($this->_pageData)) {
506
            $this->_generatePageData();
507
        }
508
        if (!empty($this->_pageData[$pageID])) {
509
            return $this->_pageData[$pageID];
510
        }
511
        return array();
512
    }
513
 
514
    // }}}
515
    // {{{ getPageIdByOffset()
516
 
517
    /**
518
     * Returns pageID for given offset
519
     *
520
     * @param $index Offset to get pageID for
521
     * @return int PageID for given offset
522
     */
523
    function getPageIdByOffset($index)
524
    {
525
        $msg = '<b>PEAR::Pager Error:</b>'
526
              .' function "getPageIdByOffset()" not implemented.';
527
        return $this->raiseError($msg, ERROR_PAGER_NOT_IMPLEMENTED);
528
    }
529
 
530
    // }}}
531
    // {{{ getOffsetByPageId()
532
 
533
    /**
534
     * Returns offsets for given pageID. Eg, if you
535
     * pass it pageID one and your perPage limit is 10
536
     * it will return (1, 10). PageID of 2 would
537
     * give you (11, 20).
538
     *
539
     * @param integer PageID to get offsets for
540
     * @return array  First and last offsets
541
     * @access public
542
     */
543
    function getOffsetByPageId($pageid = null)
544
    {
545
        $pageid = isset($pageid) ? $pageid : $this->_currentPage;
546
        if (!isset($this->_pageData)) {
547
            $this->_generatePageData();
548
        }
549
 
550
        if (isset($this->_pageData[$pageid]) || is_null($this->_itemData)) {
551
            return array(
552
                        max(($this->_perPage * ($pageid - 1)) + 1, 1),
553
                        min($this->_totalItems, $this->_perPage * $pageid)
554
                   );
555
        } else {
556
            return array(0, 0);
557
        }
558
    }
559
 
560
    // }}}
561
    // {{{ getPageRangeByPageId()
562
 
563
    /**
564
     * @param integer PageID to get offsets for
565
     * @return array  First and last offsets
566
     */
567
    function getPageRangeByPageId($pageID)
568
    {
569
        $msg = '<b>PEAR::Pager Error:</b>'
570
              .' function "getPageRangeByPageId()" not implemented.';
571
        return $this->raiseError($msg, ERROR_PAGER_NOT_IMPLEMENTED);
572
    }
573
 
574
    // }}}
575
    // {{{ getLinks()
576
 
577
    /**
578
     * Returns back/next/first/last and page links,
579
     * both as ordered and associative array.
580
     *
581
     * NB: in original PEAR::Pager this method accepted two parameters,
582
     * $back_html and $next_html. Now the only parameter accepted is
583
     * an integer ($pageID), since the html text for prev/next links can
584
     * be set in the factory. If a second parameter is provided, then
585
     * the method act as it previously did. This hack was done to mantain
586
     * backward compatibility only.
587
     *
588
     * @param integer $pageID Optional pageID. If specified, links
589
     *                for that page are provided instead of current one.  [ADDED IN NEW PAGER VERSION]
590
     * @param  string $next_html HTML to put inside the next link [deprecated: use the factory instead]
591
     * @return array back/next/first/last and page links
592
     */
593
    function getLinks($pageID=null, $next_html='')
594
    {
595
        $msg = '<b>PEAR::Pager Error:</b>'
596
              .' function "getLinks()" not implemented.';
597
        return $this->raiseError($msg, ERROR_PAGER_NOT_IMPLEMENTED);
598
    }
599
 
600
    // }}}
601
    // {{{ getCurrentPageID()
602
 
603
    /**
604
     * Returns ID of current page
605
     *
606
     * @return integer ID of current page
607
     */
608
    function getCurrentPageID()
609
    {
610
        return $this->_currentPage;
611
    }
612
 
613
    // }}}
614
    // {{{ getNextPageID()
615
 
616
    /**
617
     * Returns next page ID. If current page is last page
618
	 * this function returns FALSE
619
	 *
620
	 * @return mixed Next page ID
621
     */
622
	function getNextPageID()
623
	{
624
		return ($this->getCurrentPageID() == $this->numPages() ? false : $this->getCurrentPageID() + 1);
625
	}
626
 
627
	// }}}
628
    // {{{ getPreviousPageID()
629
 
630
    /**
631
     * Returns previous page ID. If current page is first page
632
	 * this function returns FALSE
633
	 *
634
	 * @return mixed Previous pages' ID
635
     */
636
	function getPreviousPageID()
637
	{
638
		return $this->isFirstPage() ? false : $this->getCurrentPageID() - 1;
639
	}
640
 
641
    // }}}
642
    // {{{ numItems()
643
 
644
    /**
645
     * Returns number of items
646
     *
647
     * @return int Number of items
648
     */
649
    function numItems()
650
    {
651
        return $this->_totalItems;
652
    }
653
 
654
    // }}}
655
    // {{{ numPages()
656
 
657
    /**
658
     * Returns number of pages
659
     *
660
     * @return int Number of pages
661
     */
662
    function numPages()
663
    {
664
        return (int)$this->_totalPages;
665
    }
666
 
667
    // }}}
668
    // {{{ isFirstPage()
669
 
670
    /**
671
     * Returns whether current page is first page
672
     *
673
     * @return bool First page or not
674
     */
675
    function isFirstPage()
676
    {
677
        return ($this->_currentPage < 2);
678
    }
679
 
680
    // }}}
681
    // {{{ isLastPage()
682
 
683
    /**
684
     * Returns whether current page is last page
685
     *
686
     * @return bool Last page or not
687
     */
688
    function isLastPage()
689
    {
690
        return ($this->_currentPage == $this->_totalPages);
691
    }
692
 
693
    // }}}
694
    // {{{ isLastPageComplete()
695
 
696
    /**
697
     * Returns whether last page is complete
698
     *
699
     * @return bool Last age complete or not
700
     */
701
    function isLastPageComplete()
702
    {
703
        return !($this->_totalItems % $this->_perPage);
704
    }
705
 
706
    // }}}
707
    // {{{ _generatePageData()
708
 
709
    /**
710
     * Calculates all page data
711
     * @access private
712
     */
713
    function _generatePageData()
714
    {
715
        // Been supplied an array of data?
716
        if (!is_null($this->_itemData)) {
717
            $this->_totalItems = count($this->_itemData);
718
        }
719
        $this->_totalPages = ceil((float)$this->_totalItems / (float)$this->_perPage);
720
        $i = 1;
721
        if (!empty($this->_itemData)) {
722
            foreach ($this->_itemData as $key => $value) {
723
                $this->_pageData[$i][$key] = $value;
724
                if (count($this->_pageData[$i]) >= $this->_perPage) {
725
                    $i++;
726
                }
727
            }
728
        } else {
729
            $this->_pageData = array();
730
        }
731
 
732
        //prevent URL modification
733
        $this->_currentPage = min($this->_currentPage, $this->_totalPages);
734
    }
735
 
736
    // }}}
737
    // {{{ _renderLink()
738
 
739
    /**
740
     * Renders a link using the appropriate method
741
     *
742
     * @param altText Alternative text for this link (title property)
743
     * @param linkText Text contained by this link
744
     * @return string The link in string form
745
     * @access private
746
     */
747
    function _renderLink($altText, $linkText)
748
    {
749
        if ($this->_httpMethod == 'GET') {
750
            if ($this->_append) {
751
                $href = '?' . $this->_http_build_query_wrapper($this->_linkData);
752
            } else {
753
                $href = str_replace('%d', $this->_linkData[$this->_urlVar], $this->_fileName);
754
            }
755
            return sprintf('<a href="%s"%s%s%s title="%s">%s</a>',
756
                           htmlentities($this->_url . $href),
757
                           empty($this->_classString) ? '' : ' '.$this->_classString,
758
                           empty($this->_attributes)  ? '' : ' '.$this->_attributes,
759
                           empty($this->_accesskey)   ? '' : ' accesskey="'.$this->_linkData[$this->_urlVar].'"',
760
                           $altText,
761
                           $linkText
762
            );
763
        } elseif ($this->_httpMethod == 'POST') {
764
            return sprintf("<a href='javascript:void(0)' onclick='%s'%s%s%s title='%s'>%s</a>",
765
                           $this->_generateFormOnClick($this->_url, $this->_linkData),
766
                           empty($this->_classString) ? '' : ' '.$this->_classString,
767
                           empty($this->_attributes)  ? '' : ' '.$this->_attributes,
768
                           empty($this->_accesskey)   ? '' : ' accesskey=\''.$this->_linkData[$this->_urlVar].'\'',
769
                           $altText,
770
                           $linkText
771
            );
772
        }
773
        return '';
774
    }
775
 
776
    // }}}
777
    // {{{ _generateFormOnClick()
778
 
779
    /**
780
     * Mimics http_build_query() behavior in the way the data
781
     * in $data will appear when it makes it back to the server.
782
     *  For example:
783
     * $arr =  array('array' => array(array('hello', 'world'),
784
     *                                'things' => array('stuff', 'junk'));
785
     * http_build_query($arr)
786
     * and _generateFormOnClick('foo.php', $arr)
787
     * will yield
788
     * $_REQUEST['array'][0][0] === 'hello'
789
     * $_REQUEST['array'][0][1] === 'world'
790
     * $_REQUEST['array']['things'][0] === 'stuff'
791
     * $_REQUEST['array']['things'][1] === 'junk'
792
     *
793
     * However, instead of  generating a query string, it generates
794
     * Javascript to create and submit a form.
795
     *
796
     * @param string $formAction where the form should be submitted
797
     * @param array  $data the associative array of names and values
798
     * @return string A string of javascript that generates a form and submits it
799
     * @access private
800
     */
801
    function _generateFormOnClick($formAction, $data)
802
    {
803
        // Check we have an array to work with
804
        if (!is_array($data)) {
805
            trigger_error(
806
                '_generateForm() Parameter 1 expected to be Array or Object. Incorrect value given.',
807
                E_USER_WARNING
808
            );
809
            return false;
810
        }
811
 
812
        if (!empty($this->_formID)) {
813
            $str = 'var form = document.getElementById("'.$this->_formID.'"); var input = ""; ';
814
        } else {
815
            $str = 'var form = document.createElement("form"); var input = ""; ';
816
        }
817
 
818
        // We /shouldn't/ need to escape the URL ...
819
        $str .= sprintf('form.action = "%s"; ', htmlentities($formAction));
820
        $str .= sprintf('form.method = "%s"; ', $this->_httpMethod);
821
        foreach ($data as $key => $val) {
822
            $str .= $this->_generateFormOnClickHelper($val, $key);
823
        }
824
 
825
        if (empty($this->_formID)) {
826
            $str .= 'document.getElementsByTagName("body")[0].appendChild(form);';
827
        }
828
 
829
        $str .= 'form.submit(); return false;';
830
        return $str;
831
    }
832
 
833
    // }}}
834
    // {{{ _generateFormOnClickHelper
835
 
836
    /**
837
     * This is used by _generateFormOnClick().
838
     * Recursively processes the arrays, objects, and literal values.
839
     *
840
     * @param data Data that should be rendered
841
     * @param prev The name so far
842
     * @return string A string of Javascript that creates form inputs
843
     *                representing the data
844
     * @access private
845
     */
846
    function _generateFormOnClickHelper($data, $prev = '')
847
    {
848
        $str = '';
849
        if (is_array($data) || is_object($data)) {
850
            // foreach key/visible member
851
            foreach ((array)$data as $key => $val) {
852
                // append [$key] to prev
853
                $tempKey = sprintf('%s[%s]', $prev, $key);
854
                $str .= $this->_generateFormOnClickHelper($val, $tempKey);
855
            }
856
        } else {  // must be a literal value
857
            // escape newlines and carriage returns
858
            $search  = array("\n", "\r");
859
            $replace = array('\n', '\n');
860
            $escapedData = str_replace($search, $replace, $data);
861
            // am I forgetting any dangerous whitespace?
862
            // would a regex be faster?
863
            // if it's already encoded, don't encode it again
864
            if (!$this->_isEncoded($escapedData)) {
865
                $escapedData = urlencode($escapedData);
866
            }
867
            $escapedData = htmlentities($escapedData, ENT_QUOTES, 'UTF-8');
868
 
869
            $str .= 'input = document.createElement("input"); ';
870
            $str .= 'input.type = "hidden"; ';
871
            $str .= sprintf('input.name = "%s"; ', $prev);
872
            $str .= sprintf('input.value = "%s"; ', $escapedData);
873
            $str .= 'form.appendChild(input); ';
874
        }
875
        return $str;
876
    }
877
 
878
    // }}}
879
    // {{{ _getLinksData()
880
 
881
    /**
882
     * Returns the correct link for the back/pages/next links
883
     *
884
     * @return array Data
885
     * @access private
886
     */
887
    function _getLinksData()
888
    {
889
        $qs = array();
890
        if ($this->_importQuery) {
891
            if ($this->_httpMethod == 'POST') {
892
                $qs = $_POST;
893
            } elseif ($this->_httpMethod == 'GET') {
894
                $qs = $_GET;
895
            }
896
        }
897
        if (count($this->_extraVars)){
898
            $this->_recursive_urldecode($this->_extraVars);
899
        }
900
        $qs = array_merge($qs, $this->_extraVars);
901
        foreach ($this->_excludeVars as $exclude) {
902
            if (array_key_exists($exclude, $qs)) {
903
                unset($qs[$exclude]);
904
            }
905
        }
906
        if (count($qs) && get_magic_quotes_gpc()){
907
            $this->_recursive_stripslashes($qs);
908
        }
909
        return $qs;
910
    }
911
 
912
    // }}}
913
    // {{{ _recursive_stripslashes()
914
 
915
    /**
916
     * Helper method
917
     * @param mixed $var
918
     * @access private
919
     */
920
    function _recursive_stripslashes(&$var)
921
    {
922
        if (is_array($var)) {
923
            foreach (array_keys($var) as $k) {
924
                $this->_recursive_stripslashes($var[$k]);
925
            }
926
        } else {
927
            $var = stripslashes($var);
928
        }
929
    }
930
 
931
    // }}}
932
    // {{{ _recursive_urldecode()
933
 
934
    /**
935
     * Helper method
936
     * @param mixed $var
937
     * @access private
938
     */
939
    function _recursive_urldecode(&$var)
940
    {
941
        if (is_array($var)) {
942
            foreach (array_keys($var) as $k) {
943
                $this->_recursive_urldecode($var[$k]);
944
            }
945
        } else {
946
            $trans_tbl = array_flip(get_html_translation_table(HTML_ENTITIES));
947
            $var = strtr($var, $trans_tbl);
948
        }
949
    }
950
 
951
    // }}}
952
    // {{{ _getBackLink()
953
 
954
    /**
955
     * Returns back link
956
     *
957
     * @param $url  URL to use in the link  [deprecated: use the factory instead]
958
     * @param $link HTML to use as the link [deprecated: use the factory instead]
959
     * @return string The link
960
     * @access private
961
     */
962
    function _getBackLink($url='', $link='')
963
    {
964
        //legacy settings... the preferred way to set an option
965
        //now is passing it to the factory
966
        if (!empty($url)) {
967
            $this->_path = $url;
968
        }
969
        if (!empty($link)) {
970
            $this->_prevImg = $link;
971
        }
972
        $back = '';
973
        if ($this->_currentPage > 1) {
974
            $this->_linkData[$this->_urlVar] = $this->getPreviousPageID();
975
            $back = $this->_renderLink($this->_altPrev, $this->_prevImg)
976
                  . $this->_spacesBefore . $this->_spacesAfter;
977
        }
978
        return $back;
979
    }
980
 
981
    // }}}
982
    // {{{ _getPageLinks()
983
 
984
    /**
985
     * Returns pages link
986
     *
987
     * @param $url  URL to use in the link [deprecated: use the factory instead]
988
     * @return string Links
989
     * @access private
990
     */
991
    function _getPageLinks($url='')
992
    {
993
        $msg = '<b>PEAR::Pager Error:</b>'
994
              .' function "_getPageLinks()" not implemented.';
995
        return $this->raiseError($msg, ERROR_PAGER_NOT_IMPLEMENTED);
996
    }
997
 
998
    // }}}
999
    // {{{ _getNextLink()
1000
 
1001
    /**
1002
     * Returns next link
1003
     *
1004
     * @param $url  URL to use in the link  [deprecated: use the factory instead]
1005
     * @param $link HTML to use as the link [deprecated: use the factory instead]
1006
     * @return string The link
1007
     * @access private
1008
     */
1009
    function _getNextLink($url='', $link='')
1010
    {
1011
        //legacy settings... the preferred way to set an option
1012
        //now is passing it to the factory
1013
        if (!empty($url)) {
1014
            $this->_path = $url;
1015
        }
1016
        if (!empty($link)) {
1017
            $this->_nextImg = $link;
1018
        }
1019
        $next = '';
1020
        if ($this->_currentPage < $this->_totalPages) {
1021
            $this->_linkData[$this->_urlVar] = $this->getNextPageID();
1022
            $next = $this->_spacesAfter
1023
                  . $this->_renderLink($this->_altNext, $this->_nextImg)
1024
                  . $this->_spacesBefore . $this->_spacesAfter;
1025
        }
1026
        return $next;
1027
    }
1028
 
1029
    // }}}
1030
    // {{{ _getFirstLinkTag()
1031
 
1032
    /**
1033
     * @return string
1034
     * @access private
1035
     */
1036
    function _getFirstLinkTag()
1037
    {
1038
        if ($this->isFirstPage() || ($this->_httpMethod != 'GET')) {
1039
            return '';
1040
        }
1041
        return sprintf('<link rel="first" href="%s" title="%s" />'."\n",
1042
            $this->_getLinkTagUrl(1),
1043
            $this->_firstLinkTitle
1044
        );
1045
    }
1046
 
1047
    // }}}
1048
    // {{{ _getPrevLinkTag()
1049
 
1050
    /**
1051
     * Returns previous link tag
1052
     *
1053
     * @return string the link tag
1054
     * @access private
1055
     */
1056
    function _getPrevLinkTag()
1057
    {
1058
        if ($this->isFirstPage() || ($this->_httpMethod != 'GET')) {
1059
            return '';
1060
        }
1061
        return sprintf('<link rel="previous" href="%s" title="%s" />'."\n",
1062
            $this->_getLinkTagUrl($this->getPreviousPageID()),
1063
            $this->_prevLinkTitle
1064
        );
1065
    }
1066
 
1067
    // }}}
1068
    // {{{ _getNextLinkTag()
1069
 
1070
    /**
1071
     * Returns next link tag
1072
     *
1073
     * @return string the link tag
1074
     * @access private
1075
     */
1076
    function _getNextLinkTag()
1077
    {
1078
        if ($this->isLastPage() || ($this->_httpMethod != 'GET')) {
1079
            return '';
1080
        }
1081
        return sprintf('<link rel="next" href="%s" title="%s" />'."\n",
1082
            $this->_getLinkTagUrl($this->getNextPageID()),
1083
            $this->_nextLinkTitle
1084
        );
1085
    }
1086
 
1087
    // }}}
1088
    // {{{ _getLastLinkTag()
1089
 
1090
    /**
1091
     * @return string the link tag
1092
     * @access private
1093
     */
1094
    function _getLastLinkTag()
1095
    {
1096
        if ($this->isLastPage() || ($this->_httpMethod != 'GET')) {
1097
            return '';
1098
        }
1099
        return sprintf('<link rel="last" href="%s" title="%s" />'."\n",
1100
            $this->_getLinkTagUrl($this->_totalPages),
1101
            $this->_lastLinkTitle
1102
        );
1103
    }
1104
 
1105
    // }}}
1106
    // {{{ _getLinkTagUrl()
1107
 
1108
    /**
1109
     * Helper method
1110
     * @return string the link tag url
1111
     * @access private
1112
     */
1113
    function _getLinkTagUrl($pageID)
1114
    {
1115
        $this->_linkData[$this->_urlVar] = $pageID;
1116
        if ($this->_append) {
1117
            $href = '?' . $this->_http_build_query_wrapper($this->_linkData);
1118
        } else {
1119
            $href = str_replace('%d', $this->_linkData[$this->_urlVar], $this->_fileName);
1120
        }
1121
        return htmlentities($this->_url . $href);
1122
    }
1123
 
1124
    // }}}
1125
    // {{{ getPerPageSelectBox()
1126
 
1127
    /**
1128
     * Returns a string with a XHTML SELECT menu,
1129
     * useful for letting the user choose how many items per page should be
1130
     * displayed. If parameter useSessions is TRUE, this value is stored in
1131
     * a session var. The string isn't echoed right now so you can use it
1132
     * with template engines.
1133
     *
1134
     * @param integer $start
1135
     * @param integer $end
1136
     * @param integer $step
1137
     * @param boolean $showAllData If true, perPage is set equal to totalItems.
1138
     * @param array   (or string $optionText for BC reasons)
1139
     *                - 'optionText': text to show in each option.
1140
     *                  Use '%d' where you want to see the number of pages selected.
1141
     *                - 'attributes': (html attributes) Tag attributes or
1142
     *                  HTML attributes (id="foo" pairs), will be inserted in the
1143
     *                  <select> tag
1144
     * @return string xhtml select box
1145
     * @access public
1146
     */
1147
    function getPerPageSelectBox($start=5, $end=30, $step=5, $showAllData=false, $extraParams=array())
1148
    {
1149
        require_once 'Pager/HtmlWidgets.php';
1150
        $widget =& new Pager_HtmlWidgets($this);
1151
        return $widget->getPerPageSelectBox($start, $end, $step, $showAllData, $extraParams);
1152
    }
1153
 
1154
    // }}}
1155
    // {{{ getPageSelectBox()
1156
 
1157
    /**
1158
     * Returns a string with a XHTML SELECT menu with the page numbers,
1159
     * useful as an alternative to the links
1160
     *
1161
     * @param array   - 'optionText': text to show in each option.
1162
     *                  Use '%d' where you want to see the number of pages selected.
1163
     *                - 'autoSubmit': if TRUE, add some js code to submit the
1164
     *                  form on the onChange event
1165
     * @param string   $extraAttributes (html attributes) Tag attributes or
1166
     *                  HTML attributes (id="foo" pairs), will be inserted in the
1167
     *                  <select> tag
1168
     * @return string xhtml select box
1169
     * @access public
1170
     */
1171
    function getPageSelectBox($params = array(), $extraAttributes = '')
1172
    {
1173
        require_once 'Pager/HtmlWidgets.php';
1174
        $widget =& new Pager_HtmlWidgets($this);
1175
        return $widget->getPageSelectBox($params, $extraAttributes);
1176
    }
1177
 
1178
    // }}}
1179
    // {{{ _printFirstPage()
1180
 
1181
    /**
1182
     * Print [1]
1183
     *
1184
     * @return string String with link to 1st page,
1185
     *                or empty string if this is the 1st page.
1186
     * @access private
1187
     */
1188
    function _printFirstPage()
1189
    {
1190
        if ($this->isFirstPage()) {
1191
            return '';
1192
        }
1193
        $this->_linkData[$this->_urlVar] = 1;
1194
        return $this->_renderLink(
1195
                str_replace('%d', 1, $this->_altFirst),
1196
                $this->_firstPagePre . $this->_firstPageText . $this->_firstPagePost
1197
        ) . $this->_spacesBefore . $this->_spacesAfter;
1198
    }
1199
 
1200
    // }}}
1201
    // {{{ _printLastPage()
1202
 
1203
    /**
1204
     * Print [numPages()]
1205
     *
1206
     * @return string String with link to last page,
1207
     *                or empty string if this is the 1st page.
1208
     * @access private
1209
     */
1210
    function _printLastPage()
1211
    {
1212
        if ($this->isLastPage()) {
1213
            return '';
1214
        }
1215
        $this->_linkData[$this->_urlVar] = $this->_totalPages;
1216
        return $this->_renderLink(
1217
                str_replace('%d', $this->_totalPages, $this->_altLast),
1218
                $this->_lastPagePre . $this->_lastPageText . $this->_lastPagePost
1219
        );
1220
    }
1221
 
1222
    // }}}
1223
    // {{{ _setFirstLastText()
1224
 
1225
    /**
1226
     * sets the private _firstPageText, _lastPageText variables
1227
     * based on whether they were set in the options
1228
     *
1229
     * @access private
1230
     */
1231
    function _setFirstLastText()
1232
    {
1233
        if ($this->_firstPageText == '') {
1234
            $this->_firstPageText = '1';
1235
        }
1236
        if ($this->_lastPageText == '') {
1237
            $this->_lastPageText = $this->_totalPages;
1238
        }
1239
    }
1240
 
1241
    // }}}
1242
    // {{{ _http_build_query_wrapper()
1243
 
1244
    /**
1245
     * This is a slightly modified version of the http_build_query() function;
1246
     * it heavily borrows code from PHP_Compat's http_build_query().
1247
     * The main change is the usage of htmlentities instead of urlencode,
1248
     * since it's too aggressive
1249
     *
1250
     * @author Stephan Schmidt <schst@php.net>
1251
     * @author Aidan Lister <aidan@php.net>
1252
     * @author Lorenzo Alberton <l dot alberton at quipo dot it>
1253
     * @param array $data
1254
     * @return string
1255
     * @access private
1256
     */
1257
    function _http_build_query_wrapper($data)
1258
    {
1259
        $data = (array)$data;
1260
        if (empty($data)) {
1261
            return '';
1262
        }
1263
        $separator = ini_get('arg_separator.output');
1264
        if ($separator == '&amp;') {
1265
            $separator = '&'; //the string is escaped by htmlentities anyway...
1266
        }
1267
        $tmp = array ();
1268
        foreach ($data as $key => $val) {
1269
            if (is_scalar($val)) {
1270
                //array_push($tmp, $key.'='.$val);
1271
                $val = urlencode($val);
1272
                array_push($tmp, $key .'='. str_replace('%2F', '/', $val));
1273
                continue;
1274
            }
1275
            // If the value is an array, recursively parse it
1276
            if (is_array($val)) {
1277
                array_push($tmp, $this->__http_build_query($val, htmlentities($key)));
1278
                continue;
1279
            }
1280
        }
1281
        return implode($separator, $tmp);
1282
    }
1283
 
1284
    // }}}
1285
    // {{{ __http_build_query()
1286
 
1287
    /**
1288
     * Helper function
1289
     * @author Stephan Schmidt <schst@php.net>
1290
     * @author Aidan Lister <aidan@php.net>
1291
     * @access private
1292
     */
1293
    function __http_build_query($array, $name)
1294
    {
1295
        $tmp = array ();
1296
        foreach ($array as $key => $value) {
1297
            if (is_array($value)) {
1298
                //array_push($tmp, $this->__http_build_query($value, sprintf('%s[%s]', $name, $key)));
1299
                array_push($tmp, $this->__http_build_query($value, $name.'%5B'.$key.'%5D'));
1300
            } elseif (is_scalar($value)) {
1301
                //array_push($tmp, sprintf('%s[%s]=%s', $name, htmlentities($key), htmlentities($value)));
1302
                array_push($tmp, $name.'%5B'.htmlentities($key).'%5D='.htmlentities($value));
1303
            } elseif (is_object($value)) {
1304
                //array_push($tmp, $this->__http_build_query(get_object_vars($value), sprintf('%s[%s]', $name, $key)));
1305
                array_push($tmp, $this->__http_build_query(get_object_vars($value), $name.'%5B'.$key.'%5D'));
1306
            }
1307
        }
1308
        return implode(ini_get('arg_separator.output'), $tmp);
1309
    }
1310
 
1311
    // }}}
1312
    // {{{ _isEncoded()
1313
 
1314
    /**
1315
     * Helper function
1316
     * Check if a string is an encoded multibyte string
1317
     * @param string $string
1318
     * @return boolean
1319
     * @access private
1320
     */
1321
 
1322
    function _isEncoded($string)
1323
    {
1324
        $hexchar = '&#[\dA-Fx]{2,};';
1325
        return preg_match("/^(\s|($hexchar))*$/Uims", $string) ? true : false;
1326
    }
1327
 
1328
    // }}}
1329
    // {{{ raiseError()
1330
 
1331
    /**
1332
     * conditionally includes PEAR base class and raise an error
1333
     *
1334
     * @param string $msg  Error message
1335
     * @param int    $code Error code
1336
     * @access private
1337
     */
1338
    function raiseError($msg, $code)
1339
    {
1340
        include_once 'PEAR.php';
1341
        if (empty($this->_pearErrorMode)) {
1342
            $this->_pearErrorMode = PEAR_ERROR_RETURN;
1343
        }
1344
        return PEAR::raiseError($msg, $code, $this->_pearErrorMode);
1345
    }
1346
 
1347
    // }}}
1348
    // {{{ setOptions()
1349
 
1350
    /**
1351
     * Set and sanitize options
1352
     *
1353
     * @param mixed $options    An associative array of option names and
1354
     *                          their values.
1355
     * @return integer error code (PAGER_OK on success)
1356
     * @access public
1357
     */
1358
    function setOptions($options)
1359
    {
1360
        foreach ($options as $key => $value) {
1361
            if (in_array($key, $this->_allowed_options) && (!is_null($value))) {
1362
                $this->{'_' . $key} = $value;
1363
            }
1364
        }
1365
 
1366
        //autodetect http method
1367
        if (!isset($options['httpMethod'])
1368
            && !isset($_GET[$this->_urlVar])
1369
            && isset($_POST[$this->_urlVar])
1370
        ) {
1371
            $this->_httpMethod = 'POST';
1372
        } else {
1373
            $this->_httpMethod = strtoupper($this->_httpMethod);
1374
        }
1375
 
1376
        $this->_fileName = ltrim($this->_fileName, '/');  //strip leading slash
1377
        $this->_path     = rtrim($this->_path, '/');      //strip trailing slash
1378
 
1379
        if ($this->_append) {
1380
            if ($this->_fixFileName) {
1381
                $this->_fileName = CURRENT_FILENAME; //avoid possible user error;
1382
            }
1383
            $this->_url = $this->_path.'/'.$this->_fileName;
1384
        } else {
1385
            $this->_url = $this->_path;
1386
            if (strncasecmp($this->_fileName, 'javascript', 10) != 0) {
1387
                $this->_url .= '/';
1388
            }
1389
            if (!strstr($this->_fileName, '%d')) {
1390
                trigger_error($this->errorMessage(ERROR_PAGER_INVALID_USAGE), E_USER_WARNING);
1391
            }
1392
        }
1393
 
1394
        $this->_classString = '';
1395
        if (strlen($this->_linkClass)) {
1396
            $this->_classString = 'class="'.$this->_linkClass.'"';
1397
        }
1398
 
1399
        if (strlen($this->_curPageLinkClassName)) {
1400
            $this->_curPageSpanPre  = '<span class="'.$this->_curPageLinkClassName.'">';
1401
            $this->_curPageSpanPost = '</span>';
1402
        }
1403
 
1404
        $this->_perPage = max($this->_perPage, 1); //avoid possible user errors
1405
 
1406
        if ($this->_useSessions && !isset($_SESSION)) {
1407
            session_start();
1408
        }
1409
        if (!empty($_REQUEST[$this->_sessionVar])) {
1410
            $this->_perPage = max(1, (int)$_REQUEST[$this->_sessionVar]);
1411
            if ($this->_useSessions) {
1412
                $_SESSION[$this->_sessionVar] = $this->_perPage;
1413
            }
1414
        }
1415
 
1416
        if (!empty($_SESSION[$this->_sessionVar])) {
1417
             $this->_perPage = $_SESSION[$this->_sessionVar];
1418
        }
1419
 
1420
        if ($this->_closeSession) {
1421
            session_write_close();
1422
        }
1423
 
1424
        $this->_spacesBefore = str_repeat('&nbsp;', $this->_spacesBeforeSeparator);
1425
        $this->_spacesAfter  = str_repeat('&nbsp;', $this->_spacesAfterSeparator);
1426
 
1427
        if (isset($_REQUEST[$this->_urlVar]) && empty($options['currentPage'])) {
1428
            $this->_currentPage = (int)$_REQUEST[$this->_urlVar];
1429
        }
1430
        $this->_currentPage = max($this->_currentPage, 1);
1431
        $this->_linkData = $this->_getLinksData();
1432
 
1433
        return PAGER_OK;
1434
    }
1435
 
1436
    // }}}
1437
    // {{{ getOption()
1438
 
1439
    /**
1440
     * Return the current value of a given option
1441
     *
1442
     * @param string option name
1443
     * @return mixed option value
1444
     */
1445
    function getOption($name)
1446
    {
1447
        if (!in_array($name, $this->_allowed_options)) {
1448
            $msg = '<b>PEAR::Pager Error:</b>'
1449
                  .' invalid option: '.$name;
1450
            return $this->raiseError($msg, ERROR_PAGER_INVALID);
1451
        }
1452
        return $this->{'_' . $name};
1453
    }
1454
 
1455
    // }}}
1456
    // {{{ getOptions()
1457
 
1458
    /**
1459
     * Return an array with all the current pager options
1460
     *
1461
     * @return array list of all the pager options
1462
     */
1463
    function getOptions()
1464
    {
1465
        $options = array();
1466
        foreach ($this->_allowed_options as $option) {
1467
            $options[$option] = $this->{'_' . $option};
1468
        }
1469
        return $options;
1470
    }
1471
 
1472
    // }}}
1473
    // {{{ errorMessage()
1474
 
1475
    /**
1476
     * Return a textual error message for a PAGER error code
1477
     *
1478
     * @param   int     $code error code
1479
     * @return  string  error message
1480
     * @access public
1481
     */
1482
    function errorMessage($code)
1483
    {
1484
        static $errorMessages;
1485
        if (!isset($errorMessages)) {
1486
            $errorMessages = array(
1487
                ERROR_PAGER                     => 'unknown error',
1488
                ERROR_PAGER_INVALID             => 'invalid',
1489
                ERROR_PAGER_INVALID_PLACEHOLDER => 'invalid format - use "%d" as placeholder.',
1490
                ERROR_PAGER_INVALID_USAGE       => 'if $options[\'append\'] is set to false, '
1491
                                                  .' $options[\'fileName\'] MUST contain the "%d" placeholder.',
1492
                ERROR_PAGER_NOT_IMPLEMENTED     => 'not implemented'
1493
            );
1494
        }
1495
 
1496
        return '<b>PEAR::Pager error:</b> '. (isset($errorMessages[$code]) ?
1497
            $errorMessages[$code] : $errorMessages[ERROR_PAGER]);
1498
    }
1499
 
1500
    // }}}
1501
}
1502
?>