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 : ' '); |
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' ? '&' : '&'), $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/])!', '<\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 |
?> |