/[refeed]/trunk/library/RF/Page.class.php
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/library/RF/Page.class.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 17 - (show annotations)
Sat Sep 2 18:41:14 2006 UTC (17 years, 8 months ago) by dpavlin
File size: 38974 byte(s)
fix refeed_root to be '' if it's /, so that hosting reblog on root of virtual host works
1 <?php
2 // vim: ts=4 foldcolumn=4 foldmethod=marker
3 /**
4 * RF_Page class found here.
5 *
6 * This file is part of Reblog,
7 * a derivative work of Feed On Feeds.
8 *
9 * Distributed under the Gnu Public License.
10 *
11 * @package Refeed
12 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
13 * @author Michal Migurski <mike@stamen.com>
14 * @author Michael Frumin <mfrumin@eyebeam.org>
15 * @copyright ©2004 Michael Frumin, Michal Migurski
16 * @link http://reblog.org Reblog
17 * @link http://feedonfeeds.com Feed On Feeds
18 * @version $Revision: 1.105 $
19 */
20
21 require_once('Smarty.class.php');
22
23 /**
24 * RF_Page is an extension of the Smarty template view class,
25 * with some Refeed-specific logic for generating links and such.
26 */
27 class RF_Page extends Smarty
28 {
29 /**
30 * Reference to operative controller object.
31 * @var RF_Controller
32 */
33 var $controller;
34
35 /**
36 * Reference to operative client object.
37 * @var RF_Client_Controller
38 */
39 var $client;
40
41 /**
42 * Absolute URL pointing to the path from which reBlog is operating
43 * eg: http://www.myhost.com/refeed (for a normal install)
44 * eg: http://www.myhost.com/drupal/feeds (for an in-drupal install)
45 * @var string
46 */
47 var $refeed_root;
48
49 /**
50 * Absolute URL pointing to the path from which to statically include files, like CSS and JavaScript
51 * eg: http://www.myhost.com/refeed (for a normal install)
52 * eg: http://www.myhost.com/drupal/modules/reblog/refeed (for an in-drupal install)
53 * @var string
54 */
55 var $refeed_include_root;
56
57 /**
58 * Absolute URL pointing to the current script or 'action'
59 * eg: http://www.myhost.com/refeed/view.php (for a normal install)
60 * eg: http://www.myhost.com/drupal/feeds/view (for an in-drupal install)
61 * @var string
62 */
63 var $refeed_curr_script;
64
65 /**
66 * HTTP directory of style elements, for use in templates.
67 * @var string
68 */
69 var $style_dir;
70
71 /**
72 * Smarty variable defining relative filesystem directory of template files.
73 * @var mixed
74 */
75 var $template_dir;
76
77 /** RF_Page
78 * Constructor method for RF_Page
79 *
80 * @param mixed arg if arg is a string, this is taken to be
81 * the name of the style directory.
82 *
83 * if arg is an array, this is taken to be an
84 * associative arrays of named parameters, and
85 * is extracted.
86 *
87 * @uses RF_Page::$refeed_root Initlalized to string value based on $_SERVER, assigned to Smarty variable "refeed_root"
88 * @uses RF_Page::$refeed_include_root Initlalized to string value identical to {@link RF_Page::$refeed_root refeed_root}, assigned to Smarty variable "refeed_include_root"
89 * @uses RF_Page::$refeed_curr_script Initlalized to string value based on $_SERVER, assigned to Smarty variable "refeed_curr_script"
90 * @uses RF_Page::$style_dir Initlalized to string value based on argument $arg, $_SERVER of default "style", assigned to Smarty variable "style_dir"
91 * @uses RF_Page::$template_dir Initlalized to string value based on argument $arg or "templates" inside {@link RF_Page::$style_dir style_dir}
92 * @uses RF_Page::feedsNewTitle() Assigned to Smarty function "feeds_new_title"
93 * @uses RF_Page::feedsNoNewTitle() Assigned to Smarty function "feeds_no_new_title"
94 * @uses RF_Page::viewLink() Assigned to Smarty function "view_link"
95 * @uses RF_Page::extraPageHead() Assigned to Smarty function "extra_page_head"
96 * @uses RF_Page::extraMenuMessages() Assigned to Smarty function "extra_menu_messages"
97 * @uses RF_Page::extraFeedTabs() Assigned to Smarty function "extra_feed_tabs"
98 * @uses RF_Page::extraItemTabs() Assigned to Smarty function "extra_item_tabs"
99 * @uses RF_Page::linkItem() Assigned to Smarty function "link_item"
100 * @uses RF_Page::linkFeed() Assigned to Smarty function "link_feed"
101 * @uses RF_Page::textTruncate() Assigned to Smarty modifier "text_truncate"
102 * @uses RF_Page::linkSelect() Assigned to Smarty modifier "link_select"
103 * @uses RF_Page::contentLinkSelect() Assigned to Smarty modifier "content_link_select"
104 * @uses RF_Page::feedAge() Assigned to Smarty modifier "feed_age"
105 * @uses RF_Page::balanceTags() Assigned to Smarty modifier "balance_tags"
106 * @uses RF_Page::relativeDate() Assigned to Smarty modifier "relative_date"
107 * @uses RF_Page::hilightSearchTerms() Assigned to Smarty modifier "hilight_search_terms"
108 * @uses ref_cdata_escape() Assigned to Smarty modifier "cdata_escape"
109 * @uses ref_iso86012unix_timestamp() Assigned to Smarty modifier "iso86012unix_timestamp"
110 * @uses ref_unix_timestamp2iso8601() Assigned to Smarty modifier "unix_timestamp2iso8601"
111 * @uses ref_unix_timestamp2rfc822() Assigned to Smarty modifier "unix_timestamp2rfc822"
112 */
113 function RF_Page($arg=false)
114 {
115
116 $this->error_reporting = E_ALL ^ E_NOTICE;
117
118 if($arg && is_array($arg)) {
119 extract($arg);
120
121 } elseif($arg && is_string($arg)) {
122 $style_dir = $arg;
123
124 }
125
126 $this->Smarty();
127
128 $refeed_root = get_refeed_root(dirname(dirname(dirname(__FILE__))));
129 if ($refeed_root == '/') $refeed_root = '';
130
131 $this->refeed_root = $refeed_root;
132 $this->refeed_include_root = $this->refeed_root;
133
134 $this->assign('refeed_root', $this->refeed_root);
135 $this->assign('refeed_include_root', $this->refeed_include_root);
136
137
138 $this->refeed_curr_script = "http://"
139 . $_SERVER['HTTP_HOST']
140 . rtrim((empty($_SERVER['REDIRECT_URL']) ? $_SERVER['SCRIPT_NAME'] : $_SERVER['REDIRECT_URL']));
141
142 $this->assign('refeed_curr_script', $this->refeed_curr_script);
143
144
145 // main directory that contains stylesheets, images, templates
146 $this->style_dir = !empty($style_dir)
147 ? $style_dir
148 : 'style';
149
150 $this->assign('style_dir', $this->style_dir);
151
152 // main directory that contains templates, inside style_dir by default
153 $this->template_dir = !empty($template_dir)
154 ? dirname(__FILE__)."/../../{$template_dir}"
155 : dirname(__FILE__)."/../../{$this->style_dir}/templates";
156
157 $this->compile_dir = get_configured_cache_dir();
158 $this->config_dir = dirname(__FILE__)."/../../{$this->config_dir}";
159 $this->cache_dir = get_configured_cache_dir();
160
161 $this->register_function('feeds_new_title', array(&$this, 'feedsNewTitle'));
162 $this->register_function('feeds_no_new_title', array(&$this, 'feedsNoNewTitle'));
163 $this->register_function('view_link', array(&$this, 'viewLink'));
164 $this->register_function('extra_page_head', array(&$this, 'extraPageHead'));
165 $this->register_function('extra_menu_messages', array(&$this, 'extraMenuMessages'));
166 $this->register_function('extra_feed_tabs', array(&$this, 'extraFeedTabs'));
167 $this->register_function('extra_item_tabs', array(&$this, 'extraItemTabs'));
168
169 $this->register_function('link_item', array($this, 'linkItem'));
170 $this->register_function('link_feed', array($this, 'linkFeed'));
171
172 $this->register_modifier('text_truncate', array($this, 'textTruncate'));
173 $this->register_modifier('link_select', array($this, 'linkSelect'));
174 $this->register_modifier('content_link_select', array($this, 'contentLinkSelect'));
175
176 $this->register_modifier('feed_age', array(&$this, 'feedAge'));
177 $this->register_modifier('balance_tags', array(&$this, 'balanceTags'));
178 $this->register_modifier('relative_date', array(&$this, 'relativeDate'));
179 $this->register_modifier('hilight_search_terms', array(&$this, 'hilightSearchTerms'));
180
181 $this->register_modifier('cdata_escape', 'ref_cdata_escape');
182
183 $this->register_modifier('iso86012unix_timestamp', 'ref_iso86012unix_timestamp');
184 $this->register_modifier('unix_timestamp2iso8601', 'ref_unix_timestamp2iso8601');
185 $this->register_modifier('unix_timestamp2rfc822', 'ref_unix_timestamp2rfc822');
186
187 foreach($_SERVER as $var => $value)
188 $this->assign($var, $value);
189
190 $this->assign("GLOBALS", $GLOBALS);
191 $this->assign("_SERVER", $_SERVER);
192 $this->assign("_GET", $_GET);
193 $this->assign("_POST", $_POST);
194 $this->assign("_REQUEST", $_REQUEST);
195 $this->assign("_CONSTANT", get_defined_constants());
196 $this->assign_by_ref("_SESSION", $_SESSION);
197 }
198
199 /**
200 * Returns a blob of HTML to be added to the page header, based on plugin methods
201 *
202 * @param array $p Arguments from template.
203 * Ignored completely.
204 * @return string HTML for insertion into page head
205 *
206 * @uses RF_Controller::invokePlugin() "pageHeadHTML" event, no parameters.
207 */
208 function extraPageHead($p)
209 {
210 return join("\n", $GLOBALS['REBLOG_CONTROLLER']->invokePlugin('pageHeadHTML', array()));
211 }
212
213 /**
214 * Returns a blob of HTML to be added to the sidebar menu, based on plugin methods
215 *
216 * @param array $p Arguments from template.
217 * Ignored completely.
218 * @return string HTML for insertion into sidebar menu
219 *
220 * @uses RF_Controller::invokePlugin() "menuMessageHTML" event, no parameters.
221 */
222 function extraMenuMessages($p)
223 {
224 return join("\n", $GLOBALS['REBLOG_CONTROLLER']->invokePlugin('menuMessageHTML', array()));
225 }
226
227 /**
228 * Returns a blob of HTML to be appended to feed tabs area,
229 * hopefully just a list of anchors
230 *
231 * @param array $p Arguments from template. Special elements:
232 * - "feed": {@link RF_Feed feed}
233 * @return string HTML for appending to feed tabs area
234 *
235 * @uses RF_Controller::invokePlugin() "feedTabHTML" event,
236 * parameters: {@link RF_Feed $feed}.
237 */
238 function extraFeedTabs($p)
239 {
240 return join("\n", $GLOBALS['REBLOG_CONTROLLER']->invokePlugin('feedTabHTML', array($p['feed'])));
241 }
242
243 /**
244 * Returns a blob of HTML to be appended to item tabs area,
245 * hopefully just a list of anchors
246 *
247 * @param array $p Arguments from template. Special elements:
248 * - "item": {@link RF_Item item}
249 * @return string HTML for appending to item tabs area
250 *
251 * @uses RF_Controller::invokePlugin() "itemTabHTML" event,
252 * parameters: {@link RF_Item $item}.
253 */
254 function extraItemTabs($p)
255 {
256 return join("\n", $GLOBALS['REBLOG_CONTROLLER']->invokePlugin('itemTabHTML', array($p['item'])));
257 }
258
259 /**
260 * @return string HTML attribute for targets of anchor tags
261 */
262 function linkTarget()
263 {
264 return $GLOBALS['REBLOG_USER']->linkNewWindows()
265 ? ' target="_blank" '
266 : '';
267 }
268
269 /**
270 * Generate an HTML link to a given item, used as a Smarty function.
271 *
272 * @param array $p Array of parameters passed from Smarty templates.
273 * Possible indexes:
274 * - "item" (required), {@link RF_Item item} instance
275 * - "href", link destination if not item's existing link
276 * - "title", link title (may contain HTML!) if not item's existing title
277 * - "index", integer value of link index for DOM differentiation
278 *
279 * @return string HTML anchor link
280 */
281 function linkItem($p)
282 {
283 if(empty($p['item']))
284 return '';
285
286 $index = empty($p['index'])
287 ? 0
288 : $p['index'];
289
290 $href = empty($p['href'])
291 ? $p['item']->link
292 : $p['href'];
293
294 $title = empty($p['title'])
295 ? strip_tags($p['item']->title)
296 : $p['title'];
297
298 return sprintf('<a class="headline" %s id="itemLink%d-%d" href="%s">%s</a>',
299 RF_Page::linkTarget(),
300 $p['item']->getID(),
301 $index,
302 htmlspecialchars($href),
303 $title);
304 }
305
306 /**
307 * Generate an HTML link to a given feed, used as a Smarty function.
308 *
309 * @param array $p Array of parameters passed from Smarty templates.
310 * Possible indexes:
311 * - "feed", {@link RF_Feed feed} instance
312 * - "href", link destination if not feed's existing link
313 * - "title", link title (may contain HTML!) if not feed's existing title
314 * - "description", link description if not feed's existing description
315 *
316 * @return string HTML anchor link
317 */
318 function linkFeed($p)
319 {
320 if(empty($p['feed']) && empty($p['link']))
321 return '';
322
323 $href = empty($p['href'])
324 ? $p['feed']->link
325 : $p['href'];
326
327 $title = empty($p['title'])
328 ? strip_tags($p['feed']->title)
329 : $p['title'];
330
331 $description = empty($p['description'])
332 ? $p['feed']->description
333 : $p['description'];
334
335 return sprintf('<a class="feed" %s href="%s" title="%s">%s</a>',
336 RF_Page::linkTarget(),
337 htmlspecialchars($href),
338 htmlentities($description),
339 $title);
340 return $link;
341 }
342
343 /**
344 * Truncate a chunk of text so it's less prone to causing wide,
345 * unwrappable lines of text.
346 *
347 * @param string $text Text to truncate
348 * @param int $cutoff Target length - preservation of words fudges this a bit
349 *
350 * @return string Truncated text
351 */
352 function textTruncate($text, $cutoff=64)
353 {
354 $head = floor($cutoff * 0.65);
355 $tail = floor($cutoff * 0.25);
356
357 $text = ((strlen($text) > $cutoff)
358 ? preg_replace('/^(.{'.$head.'}\S*)\s.+\s(\S*.{'.$tail.'})$/', '$1 ... $2', $text)
359 : $text);
360
361 return preg_replace('/(\S{16})/', '$1 ', $text);
362 }
363
364 /**
365 * Generate an AJAX-compliant link selection anchor for a given URL and item.
366 *
367 * @param string $href HREF to be made into a selectable link
368 * @param RF_Item $item Item whose links is to be selected
369 * @param int $index Link index, to differentiate in DOM
370 *
371 * @return string HTML anchor link
372 */
373 function linkSelect($href, $item, $index)
374 {
375 $id = 'linkSelect'.$item->getID().'-'.$index;
376
377 $new_href = sprintf('%s/do.php?action=link-select&id=%s&link=%s&return=%s',
378 $this->refeed_root,
379 $item->getID(),
380 urlencode($href),
381 urlencode($_SERVER['REQUEST_URI']));
382
383 return sprintf('<a class="link-select link-%s" id="%s" class="bold" href="%s" title="Make this hyperlink the published feed link: %s">%s</a>',
384 ($item->link == $href) ? 'selected' : 'unselected',
385 $id,
386 htmlspecialchars($new_href),
387 htmlspecialchars($href),
388 ($index < 10) ? $index : '&nbsp;');
389 }
390
391 /**
392 * Replace ordinary anchor tags in a chunk of HTML content
393 * with AJAX-compliant link-selection anchor tag pairs for a given item.
394 *
395 * @param string $content HTML content with links to be modified
396 * @param RF_Item $item Item whose content is being modified
397 *
398 * @return string HTML content with links edited to include selectors
399 *
400 * @uses RF_Page::linkSelect()
401 */
402 function contentLinkSelect($content, $item)
403 {
404 // match all anchor tags and content
405 //
406 // Andrew Sullivan has links that looks like this:
407 // <a href = http://example.com target = _blank>click here</a>
408 //
409 // BLDGBLOG sometimes omits spaces between attributes:
410 // <a href="http://example.com"target="_blank">click here</a>
411 preg_match_all('#<a\b[^>]+\bhref\s*=\s*([\'\"]?)([^\'\"]+)\1(>|\s*[^>]*>)(.*)</a>([^\s<]{0,10}?\s)??#Uis', $content, $matches);
412
413 $links = array_unique($matches[0]);
414 $hrefs = array_unique($matches[2]);
415 $texts = array_unique($matches[4]);
416
417 foreach($links as $i => $link) {
418 $link = $links[$i];
419 $href = $hrefs[$i];
420
421 $id = 'itemLink'.$item->getID().'-'.($i + 1);
422
423 // open in-post links in new windows
424 $new_link = preg_replace('/<a\s/Uis',
425 '<a id="'.$id.'" '.RF_Page::linkTarget(),
426 $link);
427
428 $content = str_replace($link,
429 $new_link.' '.$this->linkSelect($href, $item, ($i + 1)),
430 $content);
431 }
432
433 return $content;
434 }
435
436
437 /**
438 * Process a list of view arguments, filling in missing ones
439 * @static
440 *
441 * @param array $args Associative array of view arguments:
442 * - 'feed': {@link RF_Feed feed} ID
443 * - 'what': 'new' (default), 'all', 'published' or 'self'
444 * - 'when': date
445 * - 'offset': how many items back to start looking, default 0
446 * - 'howmany': how many items to return, default {@link RF_User::itemPageCount() user's item page count}
447 * - 'how': 'paged' or no? (not paged is default)
448 * - 'feedtag': feed tag to search for
449 * - 'itemtag': item tag to search for
450 * - 'search': term to match on
451 * @param boolean $full Return a full list, or just the ones specified and those that differ from the defaults?
452 *
453 * @return array Similar to input $args, but with defaults filled in
454 *
455 * @uses RF_User::itemPageCount()
456 */
457 function viewArguments($args, $full=true)
458 {
459 $outArgs = array();
460 $args = is_array($args)
461 ? $args
462 : array();
463
464 $defaults = array('feed' => null,
465 'item' => null,
466 'what' => 'all',
467 'when' => null,
468 'offset' => 0,
469 'howmany' => $GLOBALS['REBLOG_USER']->itemPageCount(),
470 'how' => null,
471 'feedtag' => null,
472 'itemtag' => null,
473 'search' => /*(isset($_SESSION['search']) ? $_SESSION['search'] :*/ null//)
474 );
475
476 foreach($defaults as $k => $v)
477 if(isset($args[$k])) {
478 if($full || $args[$k] != $v)
479 $outArgs[$k] = $args[$k];
480
481 } elseif($full) {
482 $outArgs[$k] = $v;
483
484 }
485
486 return $outArgs;
487 }
488
489 /**
490 * Returns a link to a given script with defined parameters.
491 *
492 * @param array $p Arguments from template. Special elements:
493 * - "args": A pre-existing arguments array to be overridden by other elements.
494 * - "script": Script to use as URL base; not added to query string
495 * - "context": used to determine escaping behavior, e.g. "xhtml"
496 * @return string URL for a script with given query parameters.
497 *
498 * @uses RF_Page::viewArguments()
499 * @uses RF_Client_Controller->$locations
500 */
501 function viewLink($p)
502 {
503 $args = isset($p['args']) && is_array($p['args'])
504 ? $p['args']
505 : array();
506
507 $url_args = RF_Page::viewArguments($args, false);
508
509 foreach($p as $k => $v)
510 if($k != 'args' && $k != 'script' && $k != 'context' && (!isset($args[$k]) || $v != $args[$k]))
511 $url_args[$k] = $v;
512
513 $key_values = array();
514
515 foreach($url_args as $k => $v)
516 if(is_numeric($v) || is_string($v))
517 $key_values[] = urlencode($k) . '=' . urlencode($v);
518
519 $query = count($key_values)
520 ? '?' . join(($p['context'] == 'xhtml' ? '&amp;' : '&'), $key_values)
521 : '';
522
523 // Attempt to figure out what script we're on
524 if(empty($p['script'])) {
525 // no script provided, so just use the page we're on
526 $script = $this->refeed_curr_script;
527
528 } elseif(preg_match("#^(http|/)#", $p['script'])
529 || preg_match("#^\/#", $p['script'])) {
530 // script looks like a URL, use it verbatim
531 $script = $p['script'];
532
533 } elseif(is_object($this->client)
534 && defined('REBLOG_RESPONSE'.strtoupper($p['script']))
535 && isset($this->client->locations[constant('REBLOG_RESPONSE'.strtoupper($p['script']))])) {
536
537 // $this->client is defined, we found a matching
538 // REBLOG_RESPONSE_* constant, and the corresponding
539 // index in $this->client->locations is populated
540 $script = $this->client->locations[constant('REBLOG_RESPONSE'.strtoupper($p['script']))];
541
542 } elseif(is_object($this->client)
543 && isset($this->client->locations[$p['script']])) {
544
545 // $this->client is defined, and the corresponding
546 // index in $this->client->locations is populated
547 $script = $this->client->locations[$p['script']];
548
549 } else {
550 // just treat it like a local file
551 $script = $this->refeed_root . '/' . $p['script'];
552
553 }
554
555 return $script . $query;
556 }
557
558
559 /**
560 * Returns a title suitable for labeling a set of feeds matching some critera
561 *
562 * @param array $p Arguments from template
563 * @return string Title for a set of feeds with new content matching some criteria
564 */
565 function feedsNewTitle($p)
566 {
567 if($p['args']['search'])
568 return htmlentities("Feeds with items matching \"{$p['args']['search']}\"");
569
570 if($p['args']['feedtag'])
571 return htmlentities("Feeds tagged \"{$p['args']['feedtag']}\"");
572
573 return "Feeds with new items";
574 }
575
576 /**
577 * Returns a title suitable for labeling a set of feeds not matching some critera
578 *
579 * @param array $p Arguments from template
580 * @return string Title for a set of feeds with new content not matching some criteria
581 */
582 function feedsNoNewTitle($p)
583 {
584 if($p['args']['search'])
585 return htmlentities("Feeds with no items matching \"{$p['args']['search']}\"");
586
587 if($p['args']['feedtag'])
588 return htmlentities("Feeds not tagged \"{$p['args']['feedtag']}\"");
589
590 return "Feeds with no new items";
591 }
592
593 /**
594 * Concoct an appropriate title for the current item list.
595 *
596 * @param RF_Feed $feed Feed being viewed, if any
597 * @param array $args Name-value pairs of other parameters
598 *
599 * @return string Appropriate description of the items currently shown.
600 */
601 function viewTitle($feed=null, $args)
602 {
603 $title = '';
604
605 switch($args['what']) {
606 case 'new':
607 $title .= 'Unread items';
608 break;
609 case 'published':
610 $title .= 'Published items';
611 break;
612 case 'self':
613 $title .= 'My posted items';
614 break;
615 default:
616 $title .= 'Items';
617 break;
618 }
619
620 if(!empty($args['itemtag']))
621 $title .= sprintf(' tagged with <em>"%s"</em>', htmlspecialchars($args['itemtag']));
622
623 if(!empty($args['feedtag']))
624 $title .= sprintf(' in feeds tagged with <em>"%s"</em>', htmlspecialchars($args['feedtag']));
625
626 if(!empty($args['search']))
627 $title .= sprintf(' matching <em>"%s"</em>', htmlspecialchars($args['search']));
628
629 switch($args['when']) {
630 case 'today':
631 $title .= " from today";
632 break;
633 default:
634 if(!empty($args['when']) && $time = strtotime($args['when']))
635 $title .= ' from ' . date('M j, Y', $time);
636 break;
637 }
638
639 if(!empty($args['feed']))
640 $title .= sprintf(' in feed <em>"%s"</em>', htmlspecialchars($feed->title));
641
642 $start = $args['offset'] + 1;
643 $end = $args['offset'] + $args['howmany'];
644
645 $title .= !empty($args['how']) && $args['how'] == 'paged'
646 ? " (items {$start} to {$end})"
647 : '';
648
649 return trim($title);
650 }
651
652 /**
653 * Concoct an appropriate set of paging links for the current item list.
654 *
655 * @param RF_Feed $feed Feed being viewed, if any
656 * @param array $args Name-value pairs of other parameters
657 * @param boolean $include_all Include the "all" links at the end?
658 *
659 * @return string HTML paging links for the current item list.
660 *
661 * @uses RF_Page::viewLink()
662 */
663 function itemNavLinks($feed=null, $args, $include_all=true)
664 {
665 switch($args['what']) {
666 case 'new':
667 $all_HTML = 'All unread items';
668 break;
669 case 'published':
670 $all_HTML = 'All published items';
671 break;
672 case 'self':
673 $all_HTML = 'All of my posted items';
674 break;
675 default:
676 $all_HTML = 'All items';
677 break;
678 }
679
680 if(!empty($args['itemtag']))
681 $all_HTML .= sprintf(' tagged with <em>"%s"</em>', htmlspecialchars($args['itemtag']));
682
683 if(!empty($args['search']))
684 $all_HTML .= sprintf(' matching <em>"%s"</em>', htmlspecialchars($args['search']));
685
686 $all_HTML .= empty($feed)
687 ? ''
688 : sprintf(' from feed <em>"%s"</em>', htmlspecialchars($feed->title));
689
690 $links = array();
691 $args['script'] = empty($args['script']) ? '_ITEM_LIST' : $args['script'];
692 $args['context'] = 'xhtml';
693
694 if(!empty($args['when'])) {
695
696 $whendate = ($args['when'] == 'today')
697 ? date('Y/m/d')
698 : $args['when'];
699
700 $begin = strtotime($whendate);
701 $end = $begin + (24 * 60 * 60);
702
703 $tomorrow = date('M j, Y', $begin + (24 * 60 * 60));
704 $yesterday = date('M j, Y', $begin - (24 * 60 * 60));
705
706 $links[] = sprintf("<a href=\"%s\">{$yesterday}</a>", $this->viewLink(array_merge($args, array('when' => $yesterday))));
707
708 if($args['when'] != "today") {
709 $links[] = sprintf("<a href=\"%s\">Today</a>", $this->viewLink(array_merge($args, array('when' => 'today'))));
710 $links[] = sprintf("<a href=\"%s\">{$tomorrow} {$limit}</a>", $this->viewLink(array_merge($args, array('when' => $tomorrow))));
711 }
712
713 if($include_all)
714 $links[] = sprintf("<a href=\"%s\">{$all_HTML}</a>", $this->viewLink(array_merge($args, array('when' => null))));
715
716 } elseif(!empty($args['how']) && $args['how'] == "paged") {
717
718 $limit = $args['howmany'];
719 $start = $args['offset'];
720
721 $earlier = $start + $limit;
722 $later = $start - $limit;
723
724 $links[] = sprintf("<a href=\"%s\">Older {$limit}</a>", $this->viewLink(array_merge($args, array('offset' => $earlier, 'howmany' => $limit))));
725
726 if($later >= 0) {
727 $links[] = sprintf("<a href=\"%s\">Newest {$limit}</a>", $this->viewLink(array_merge($args, array('offset' => $which, 'howmany' => $limit))));
728 $links[] = sprintf("<a href=\"%s\">Newer {$limit}</a>", $this->viewLink(array_merge($args, array('offset' => $later, 'howmany' => $limit))));
729 }
730
731 if($include_all)
732 $links[] = sprintf("<a href=\"%s\">{$all_HTML}</a>", $this->viewLink(array_merge($args, array('how' => null, 'offset' => null, 'howmany' => null))));
733
734 } else {
735
736 return '';
737
738 }
739
740 return $links;
741 }
742
743 /**
744 * Returns a feed's age.
745 *
746 * @param integer $timestamp Unix timestamp of feed's age
747 * @return string HTML snippet that shows a feed's age
748 */
749 function feedAge($timestamp)
750 {
751 $diff = time() - $timestamp;
752 $diffMarkup = '';
753
754 if(date('Y-m-d', $timestamp) == date('Y-m-d'))
755 $diffMarkup = '<span class="today">Today</span>';
756
757 if($diff < 60 * 60)
758 $diffMarkup = '<span class="last-hour">Last Hour</span>';
759
760 if($days = floor($diff / 60 / 60 / 24))
761 $diffMarkup = date('M j', $timestamp);
762
763 if(date('Y-m-d', $timestamp) == date('Y-m-d', time() - (60 * 60 * 24)))
764 $diffMarkup = '<span class="yesterday">Yesterday</span>';
765
766 if(empty($diffMarkup))
767 $diffMarkup = date('M j, Y', $timestamp);
768
769 if($timestamp == 0)
770 $diffMarkup = '<span class="never">Never</span>';
771
772 return $diffMarkup;
773 }
774
775 /**
776 * Returns a relative date string.
777 *
778 * @param integer $timestamp Unix timestamp
779 * @return string String that shows an approximate time difference from now, e.g. "20 minutes ago"
780 */
781 function relativeDate($timestamp)
782 {
783 $diff = time() - $timestamp;
784 $diffstr = '???';
785
786 if($seconds = $diff)
787 $diffstr = sprintf('%d second%s ago', $seconds, (($seconds != 1) ? 's' : ''));
788
789 if($minutes = round($diff / 60))
790 $diffstr = sprintf('%d minute%s ago', $minutes, (($minutes != 1) ? 's' : ''));
791
792 if($hours = round($diff / 60 / 60))
793 $diffstr = sprintf('%d hour%s ago', $hours, (($hours != 1) ? 's' : ''));
794
795 if($days = floor($diff / 60 / 60 / 24))
796 $diffstr = date('F j', $timestamp);
797
798 if($seasons = floor($diff / 60 / 60 / 24 / 90))
799 $diffstr = date('F j, Y', $timestamp);
800
801 if($timestamp == 0)
802 $diffstr = 'never';
803
804 return $diffstr;
805 // date_format:"%b %e, %Y, %H:%M"
806 }
807
808 function hilightSearchTerms($content)
809 {
810 /*
811 if(!empty($_GET['search']) && preg_match_all('/\b[\-\+]?\S+\b/', $_GET['search'], $matches)) {
812 foreach($matches[0] as $term) {
813 $term = preg_quote($term);
814 $content = preg_replace("/\b({$term})\b/Uis", '<span class="search-term">\1</span>', $content);
815
816 // if we accidentally dropped any span's inside a tag, they ought to be removed
817 while(preg_match('#(<[^>]+)<span class="search-term">([^<]+)</span>#', $content))
818 $content = preg_replace('#(<[^>]+)<span class="search-term">([^<]+)</span>#', '\1\2', $content);
819 }
820 }
821 */
822
823 return $content;
824 }
825
826 /**
827 * Balance tags in a chunk of HTML.
828 *
829 * Ensure that all HTML is valid XHTML.
830 * Duplicated from Steven Wittens' <unconed@drop.org>
831 * HTML Corrector Drupal module, licensed under GPL.
832 *
833 * @see http://drupal.org/project/htmlcorrector
834 *
835 * @param string $HTML HTML to be balanced
836 * @return string Balanced HTML
837 */
838 function balanceTags($text)
839 {
840 // Tags which cannot be nested but are typically left unclosed.
841 $nonesting = array('li', 'p');
842
843 // Single use tags in HTML4
844 $singleuse = array('base', 'meta', 'link', 'hr', 'br', 'param', 'img', 'area', 'input', 'col', 'frame');
845
846 // Properly entify angles
847 $text = preg_replace('!<([^a-zA-Z/])!', '&lt;\1', $text);
848
849 // Splits tags from text
850 $split = preg_split('/<([^>]+?)>/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
851 // Note: PHP ensures the array consists of alternating delimiters and literals
852 // and begins and ends with a literal (inserting $null as required).
853
854 $tag = false; // Odd/even counter. Tag or no tag.
855 $stack = array();
856 $output = '';
857 foreach ($split as $value) {
858 // HTML tag
859 if ($tag) {
860
861 // changed explode() to preg_split() here, for feeds with tags broken onto multiple lines
862 list($tagname) = preg_split('#\s+#', strtolower($value), 2);
863
864 // Closing tag
865 if ($tagname{0} == '/') {
866 $tagname = substr($tagname, 1);
867 if (!in_array($tagname, $singleuse)) {
868 // See if we have other tags lingering first, and close them
869 while (($stack[0] != $tagname) && count($stack)) {
870 $output .= '</'. array_shift($stack) .'>';
871 }
872 // If the tag was not found, just leave it out;
873 if (count($stack)) {
874 $output .= '</'. array_shift($stack) .'>';
875 }
876 }
877 }
878 // Opening tag
879 else {
880 // See if we have an identical tag already open and close it if desired.
881 if (count($stack) && ($stack[0] == $tagname) && in_array($stack[0], $nonesting)) {
882 $output .= '</'. array_shift($stack) .'>';
883 }
884 // Push non-single-use tags onto the stack
885 if (!in_array($tagname, $singleuse)) {
886 array_unshift($stack, $tagname);
887 }
888 // Add trailing slash to single-use tags as per X(HT)ML.
889 else {
890 $value = rtrim($value, ' /') . ' /';
891 }
892 $output .= '<'. $value .'>';
893 }
894 }
895 else {
896 // Passthrough
897 $output .= $value;
898 }
899 $tag = !$tag;
900 }
901 // Close remaining tags
902 while (count($stack) > 0) {
903 $output .= '</'. array_shift($stack) .'>';
904 }
905 return $output;
906 }
907
908 function render_feed_link($feed)
909 {
910 $link = htmlspecialchars($feed->link);
911 $description = htmlspecialchars($feed->description);
912 $title = htmlspecialchars($feed->title);
913 $url = htmlspecialchars($feed->url);
914
915 $s = "<b><a href=\"$link\" title=\"$description\">$title</a></b> ";
916 $s .= "<a href=\"$url\">(rss)</a>";
917
918 return $s;
919 }
920
921
922
923 function add_template_dir($dir, $where = 'pre') {
924 if(!is_array($this->template_dir)) {
925 $arr = Array();
926 $arr[] = $this->template_dir;
927 $this->template_dir = $arr;
928 }
929
930 if($where == 'post') {
931 array_push($this->template_dir, $dir);
932 }
933 else {
934 array_unshift($this->template_dir, $dir);
935 }
936
937 }
938
939
940
941 }
942
943 ?>

  ViewVC Help
Powered by ViewVC 1.1.26