PageRenderTime 37ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/inc/core.php

https://github.com/dshafik/wp-lifestream
PHP | 2783 lines | 2311 code | 250 blank | 222 comment | 257 complexity | 777786d487d841862154389770d25cb5 MD5 | raw file
  1. <?php
  2. if (!class_exists('SimplePie'))
  3. {
  4. require_once(LIFESTREAM_PATH . '/lib/simplepie.inc.php');
  5. }
  6. global $wpdb, $userdata, $lifestream;
  7. function lifestream_path_join()
  8. {
  9. $bits = func_get_args();
  10. $sep = (in_array(PHP_OS, array("WIN32", "WINNT")) ? '\\' : '/');
  11. foreach ($bits as $key=>$value) {
  12. $bits[$key] = rtrim($value, $sep);
  13. }
  14. return implode($sep, $bits);
  15. }
  16. function lifestream_array_key_pop($array, $key)
  17. {
  18. $value = @$array[$key];
  19. unset($array[$key]);
  20. return $value;
  21. }
  22. // Returns the utf string corresponding to the unicode value (from php.net, courtesy - romans@void.lv)
  23. function lifestream_code2utf($num)
  24. {
  25. if ($num < 128) return chr($num);
  26. if ($num < 2048) return chr(($num >> 6) + 192) . chr(($num & 63) + 128);
  27. if ($num < 65536) return chr(($num >> 12) + 224) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128);
  28. if ($num < 2097152) return chr(($num >> 18) + 240) . chr((($num >> 12) & 63) + 128) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128);
  29. return '';
  30. }
  31. function lifestream_str_startswith($string, $chunk)
  32. {
  33. return substr($string, 0, strlen($chunk)) == $chunk;
  34. }
  35. function lifestream_str_endswith($string, $chunk)
  36. {
  37. return substr($string, strlen($chunk)*-1) == $chunk;
  38. }
  39. function lifestream_get_class_constant($class, $const)
  40. {
  41. return constant(sprintf('%s::%s', $class, $const));
  42. }
  43. class Lifestream_Error extends Exception { }
  44. class Lifestream_ValidationError extends Exception { }
  45. class Lifestream_FeedFetchError extends Lifestream_Error { }
  46. class Lifestream_Event
  47. {
  48. /**
  49. * Represents a single event in the database.
  50. */
  51. function __construct(&$lifestream, $row)
  52. {
  53. $this->lifestream = $lifestream;
  54. $this->date = $row->timestamp;
  55. $this->data = array(unserialize($row->data));
  56. $this->id = $row->id;
  57. $this->timestamp = $row->timestamp;
  58. $this->total = 1;
  59. $this->is_grouped = false;
  60. $this->key = $row->key;
  61. $this->owner = $row->owner;
  62. $this->owner_id = $row->owner_id;
  63. $this->visible = $row->visible;
  64. $this->link = @(!empty($this->data['link']) ? $this->data['link'] : $row->link);
  65. $cls = $this->lifestream->get_feed($row->feed);
  66. $this->feed = new $cls($this->lifestream, unserialize($row->options), $row->feed_id);
  67. }
  68. function __toString()
  69. {
  70. return $this->data[0]['title'];
  71. }
  72. function get_event_display()
  73. {
  74. return $this->feed->get_event_display($this, $this->data[0]);
  75. }
  76. function get_event_link()
  77. {
  78. return $this->feed->get_event_link($this, $this->data[0]);
  79. }
  80. function get_timesince()
  81. {
  82. return $this->lifestream->timesince($this->timestamp);
  83. }
  84. function get_date()
  85. {
  86. return $this->date + LIFESTREAM_DATE_OFFSET;
  87. }
  88. /**
  89. * Returns an HTML-ready string.
  90. */
  91. function render($options=array())
  92. {
  93. return $this->feed->render($this, $options);
  94. }
  95. function get_label_instance($options=array())
  96. {
  97. if (!isset($this->_label_instance))
  98. {
  99. $this->_label_instance = $this->feed->get_label($this, $options);
  100. }
  101. return $this->_label_instance;
  102. }
  103. function get_label($options=array())
  104. {
  105. $label_inst = $this->get_label_instance($options);
  106. if (count($this->data) > 1)
  107. {
  108. if (@$options['show_owners'] || $this->lifestream->get_option('show_owners'))
  109. {
  110. $label = $label_inst->get_label_plural_user();
  111. }
  112. else
  113. {
  114. $label = $label_inst->get_label_plural();
  115. }
  116. }
  117. else
  118. {
  119. if (@$options['show_owners'] || $this->lifestream->get_option('show_owners'))
  120. {
  121. $label = $label_inst->get_label_single_user();
  122. }
  123. else
  124. {
  125. $label = $label_inst->get_label_single();
  126. }
  127. }
  128. return $label;
  129. }
  130. function get_feed_label($options=array())
  131. {
  132. $label_inst = $this->get_label_instance($options);
  133. return $label_inst->get_feed_label();
  134. }
  135. function get_url()
  136. {
  137. if (count($this->data) > 1)
  138. {
  139. // return the public url if it's grouped
  140. $url = $this->feed->get_public_url();
  141. if ($url) return $url;
  142. }
  143. else
  144. {
  145. $url = $this->data[0]['link'];
  146. if ($url) return $url;
  147. }
  148. return '#';
  149. }
  150. }
  151. class Lifestream_EventGroup extends Lifestream_Event
  152. {
  153. /**
  154. * Represents a grouped event in the database.
  155. */
  156. function __construct(&$lifestream, $row)
  157. {
  158. parent::__construct($lifestream, $row);
  159. $this->total = $row->total ? $row->total : 1;
  160. $this->data = unserialize($row->data);
  161. $this->is_grouped = true;
  162. }
  163. function get_event_display($bit)
  164. {
  165. return $this->feed->get_event_display($this, $bit);
  166. }
  167. function get_event_link($bit)
  168. {
  169. return $this->feed->get_event_link($this, $bit);
  170. }
  171. }
  172. class Lifestream
  173. {
  174. // stores all registered feeds
  175. public $feeds = array();
  176. // stores file locations to feed classes
  177. public $paths = array();
  178. // stores theme information
  179. public $themes = array();
  180. // stores icon folder names
  181. public $icons = array();
  182. // current theme
  183. public $theme = 'default';
  184. protected $paging_key = 'ls_p';
  185. protected $valid_image_types = array('image/gif' => 'gif',
  186. 'image/jpeg' => 'jpeg',
  187. 'image/png' => 'png',
  188. 'image/gif' => 'gif',
  189. 'image/x-icon' => 'ico',
  190. 'image/bmp' => 'bmp',
  191. 'image/vnd.microsoft.icon' => 'ico'
  192. );
  193. protected $valid_image_extensions = array(
  194. 'gif', 'jpg', 'jpeg', 'gif', 'png', 'ico'
  195. );
  196. function html_entity_decode($string)
  197. {
  198. static $trans_tbl;
  199. // replace numeric entities
  200. $string = preg_replace('~&#x([0-9a-f]+);~ei', 'lifestream_code2utf(hexdec("\\1"))', $string);
  201. $string = preg_replace('~&#([0-9]+);~e', 'lifestream_code2utf(\\1)', $string);
  202. // replace literal entities
  203. if (!isset($trans_tbl))
  204. {
  205. $trans_tbl = array();
  206. foreach (get_html_translation_table(HTML_ENTITIES) as $val=>$key)
  207. $trans_tbl[$key] = utf8_encode($val);
  208. }
  209. return strtr($string, $trans_tbl);
  210. }
  211. // function html_entity_decode($string)
  212. // {
  213. // $string = html_entity_decode($string, ENT_QUOTES, 'utf-8');
  214. //
  215. // $string = preg_replace('~&#x0*([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string);
  216. // $string = preg_replace('~&#0*([0-9]+);~e', 'chr(\\1)', $string);
  217. //
  218. // return $string;
  219. // }
  220. function parse_nfo_file($file)
  221. {
  222. $data = array();
  223. if (!is_file($file)) return $data;
  224. $fp = file($file);
  225. foreach ($fp as $line)
  226. {
  227. if (lifestream_str_startswith('#', $line)) continue;
  228. list($key, $value) = explode(':', $line, 2);
  229. $data[strtolower($key)] = trim($value);
  230. }
  231. return $data;
  232. }
  233. function get_icon_paths()
  234. {
  235. $directories = array(
  236. lifestream_path_join(LIFESTREAM_PATH, 'icons')
  237. );
  238. if ($this->get_option('icon_dir') && $this->get_option('icon_dir') != $directories[0]) {
  239. $directories[] = $this->get_option('icon_dir');
  240. }
  241. return $directories;
  242. }
  243. function get_rss_feed_url()
  244. {
  245. $permalink = get_option('permalink_structure');
  246. if (!empty($permalink))
  247. {
  248. $url = trailingslashit(get_bloginfo('rss2_url')) . 'lifestream-feed';
  249. }
  250. else {
  251. $url = trailingslashit(get_bloginfo('url')) . 'wp-rss2.php?feed=lifestream-feed';
  252. }
  253. return $url;
  254. }
  255. /**
  256. * Find each icons/name/generic.png file.
  257. */
  258. function detect_icons()
  259. {
  260. $directories = $this->get_icon_paths();
  261. foreach ($directories as $base_dir)
  262. {
  263. if (!is_dir($base_dir)) continue;
  264. $handler = opendir($base_dir);
  265. while ($file = readdir($handler))
  266. {
  267. // ignore hidden files
  268. if (lifestream_str_startswith($file, '.')) continue;
  269. // if its not a directory we dont care
  270. $path = lifestream_path_join($base_dir, $file);
  271. if (!is_dir($path)) continue;
  272. $ext_file = lifestream_path_join($path, 'generic.png');
  273. if (is_file($ext_file))
  274. {
  275. $data = $this->parse_nfo_file(lifestream_path_join($path, 'icons.txt'));
  276. if (!$data['name']) $data['name'] = $file;
  277. $data['__path'] = $path;
  278. $data['__url'] =
  279. $this->icons[$file] = $data;
  280. }
  281. }
  282. }
  283. }
  284. function get_extension_paths()
  285. {
  286. $directories = array(
  287. lifestream_path_join(LIFESTREAM_PATH, 'extensions')
  288. );
  289. if ($this->get_option('extension_dir') && $this->get_option('extension_dir') != $directories[0]) {
  290. $directories[] = $this->get_option('extension_dir');
  291. }
  292. return $directories;
  293. }
  294. /**
  295. * Find each extension/name/extension.inc.php file.
  296. */
  297. function detect_extensions()
  298. {
  299. $lifestream =& $this;
  300. $directories = $this->get_extension_paths();
  301. foreach ($directories as $base_dir)
  302. {
  303. if (!is_dir($base_dir)) continue;
  304. $handler = opendir($base_dir);
  305. while ($file = readdir($handler))
  306. {
  307. // ignore hidden files
  308. if (lifestream_str_startswith($file, '.')) continue;
  309. $path = lifestream_path_join($base_dir, $file);
  310. // if its not a directory we dont care
  311. if (!is_dir($path)) continue;
  312. // check for extension.inc.php
  313. $ext_file = lifestream_path_join($path, 'extension.inc.php');
  314. if (is_file($ext_file))
  315. {
  316. include($ext_file);
  317. }
  318. }
  319. }
  320. }
  321. function get_theme_paths()
  322. {
  323. $directories = array(
  324. lifestream_path_join(LIFESTREAM_PATH, 'themes')
  325. );
  326. if ($this->get_option('theme_dir') && $this->get_option('theme_dir') != $directories[0]) {
  327. $directories[] = $this->get_option('theme_dir');
  328. }
  329. return $directories;
  330. }
  331. /**
  332. * Find each themes/name/theme.txt file.
  333. */
  334. function detect_themes()
  335. {
  336. $directories = $this->get_theme_paths();
  337. foreach ($directories as $base_dir)
  338. {
  339. if (!is_dir($base_dir)) continue;
  340. $handler = opendir($base_dir);
  341. while ($file = readdir($handler))
  342. {
  343. // ignore hidden files
  344. if (lifestream_str_startswith($file, '.')) continue;
  345. // if its not a directory we dont care
  346. $path = lifestream_path_join($base_dir, $file);
  347. if (!is_dir($path)) continue;
  348. // check for main.inc.php
  349. $ext_file = lifestream_path_join($path, 'theme.txt');
  350. if (is_file($ext_file))
  351. {
  352. $theme = array();
  353. $theme = $this->parse_nfo_file($ext_file);
  354. $theme['__path'] = $path;
  355. if (!array_key_exists('name', $theme)) continue;
  356. $this->themes[$file] = $theme;
  357. }
  358. }
  359. }
  360. }
  361. function get_media_url_for_icon($filename='generic.png', $iconpack='default')
  362. {
  363. $path = lifestream_path_join($this->icons[$iconpack]['__path'], $filename);
  364. if (!is_file($path))
  365. {
  366. $filename = 'generic.png';
  367. $path = lifestream_path_join(LIFESTREAM_PATH, 'icons', 'default', $filename);
  368. }
  369. return $this->get_absolute_media_url($path);
  370. }
  371. function get_icon_media_url($filename)
  372. {
  373. return $this->get_media_url_for_icon($filename, $this->get_option('icons', 'default'));
  374. }
  375. function get_theme_media_url($filename)
  376. {
  377. return $this->get_media_url_for_theme($filename, $this->get_option('theme', 'default'));
  378. }
  379. function get_media_url_for_theme($filename, $theme='default')
  380. {
  381. // base dir is now $theme['__path'] so we must abstract the web dir
  382. $path = lifestream_path_join($this->themes[$theme]['__path'], 'media', $filename);
  383. if (!is_file($path))
  384. {
  385. $path = lifestream_path_join(LIFESTREAM_PATH, 'themes', 'default', 'media', $filename);
  386. }
  387. return $this->get_absolute_media_url($path);
  388. }
  389. function get_absolute_media_url($path)
  390. {
  391. // XXX: This will fail if you're symlinking the lifestream directory in
  392. $path = str_replace(trailingslashit(WP_CONTENT_DIR), '', $path);
  393. return str_replace('\\', '/', trailingslashit(WP_CONTENT_URL).$path);
  394. }
  395. function get_theme_filepath($filename)
  396. {
  397. $path = $this->get_filepath_for_theme($filename, $this->get_option('theme', 'default'));
  398. if (!is_file($path))
  399. {
  400. $path = $this->get_filepath_for_theme($filename, 'default');
  401. }
  402. return $path;
  403. }
  404. function get_filepath_for_theme($filename, $theme='default')
  405. {
  406. if (!array_key_exists($theme, $this->themes))
  407. {
  408. throw new Exception('Theme is not valid.');
  409. }
  410. return lifestream_path_join($this->themes[$theme]['__path'], $filename);
  411. }
  412. function validate_image($url)
  413. {
  414. // // Check the extension
  415. // $bits = explode('.', basename($url));
  416. // if (count($bits) > 1)
  417. // {
  418. // $ext = $bits[count($bits)-1];
  419. // return (in_array($ext, $this->valid_image_extensions));
  420. // }
  421. $handler = $this->get_option('url_handler');
  422. $use_fsock = true;
  423. if (($handler == 'auto' && function_exists('curl_init')) || $handler == 'curl')
  424. {
  425. $use_fsock = false;
  426. }
  427. $file = new SimplePie_File($url, 10, 5, null, SIMPLEPIE_USERAGENT, $use_fsock);
  428. if (!$file->success)
  429. {
  430. return false;
  431. }
  432. // Attempt to check content type
  433. if (!empty($file->headers['content-type']))
  434. {
  435. return (in_array($file->headers['content-type'], $this->valid_image_types));
  436. }
  437. // Use GD if we can
  438. if (function_exists('imagecreatefromstring'))
  439. {
  440. return (imagecreatefromstring($file->body) !== false);
  441. }
  442. // Everything has failed, we'll just let it pass
  443. return true;
  444. }
  445. // options and their default values
  446. protected $_options = array(
  447. 'day_format' => 'F jS',
  448. 'hour_format' => 'g:ia',
  449. 'number_of_items' => '50',
  450. 'date_interval' => '1 month',
  451. 'digest_title' => 'Daily Digest for %s',
  452. 'digest_body' => '%1$s',
  453. 'digest_category' => '1',
  454. 'digest_author' => '1',
  455. 'daily_digest' => '0',
  456. 'digest_interval' => 'daily',
  457. 'digest_time' => '0',
  458. 'update_interval' => '15',
  459. 'show_owners' => '0',
  460. 'use_ibox' => '1',
  461. 'show_credits' => '1',
  462. 'hide_details_default' => '1',
  463. 'url_handler' => 'auto',
  464. 'feed_items' => '10',
  465. 'truncate_length' => '128',
  466. 'theme' => 'default',
  467. 'icons' => 'default',
  468. 'extension_dir' => '',
  469. 'theme_dir' => '',
  470. 'icon_dir' => '',
  471. 'links_new_windows' => '0',
  472. 'truncate_interval' => '0',
  473. );
  474. function __construct()
  475. {
  476. $this->path = WP_CONTENT_URL . '/plugins/lifestream';
  477. $this->_optioncache = null;
  478. add_action('admin_menu', array(&$this, 'options_menu'));
  479. add_action('wp_head', array(&$this, 'header'));
  480. add_filter('the_content', array(&$this, 'embed_callback'));
  481. add_action('init', array(&$this, 'init'));
  482. add_filter('cron_schedules', array(&$this, 'get_cron_schedules'));
  483. add_action('lifestream_digest_cron', array(&$this, 'digest_update'));
  484. add_action('lifestream_cron', array(&$this, 'update'));
  485. add_action('lifestream_cleanup', array(&$this, 'cleanup_history'));
  486. register_activation_hook(LIFESTREAM_PLUGIN_FILE, array(&$this, 'activate'));
  487. register_deactivation_hook(LIFESTREAM_PLUGIN_FILE, array(&$this, 'deactivate'));
  488. }
  489. function truncate($string, $length=128)
  490. {
  491. if (!($length > 0)) return $string;
  492. if (strlen($string) > $length)
  493. {
  494. $string = substr($string, 0, $length-3).'...';
  495. }
  496. return $string;
  497. }
  498. // To be quite honest, WordPress should be doing this kind of magic itself.
  499. function _populate_option_cache()
  500. {
  501. if (!$this->_optioncache)
  502. {
  503. $this->_optioncache = (array)get_option('lifestream_options');
  504. if (!$this->_optioncache) $this->_optioncache = (array)$this->_options;
  505. }
  506. }
  507. /**
  508. * Fetches the value of an option. Returns `null` if the option is not set.
  509. */
  510. function get_option($option, $default=null)
  511. {
  512. $this->_populate_option_cache();
  513. if (!isset($this->_optioncache[$option])) $value = $default;
  514. else
  515. {
  516. $value = $this->_optioncache[$option];
  517. }
  518. if (empty($value)) $value = $default;
  519. return $value;
  520. }
  521. /**
  522. * Removes an option.
  523. */
  524. function delete_option($option)
  525. {
  526. $this->_populate_option_cache();
  527. unset($this->_optioncache[$option]);
  528. update_option('lifestream_options', $this->_optioncache);
  529. }
  530. /**
  531. * Updates the value of an option.
  532. */
  533. function update_option($option, $value)
  534. {
  535. $this->_populate_option_cache();
  536. $this->_optioncache[$option] = $value;
  537. update_option('lifestream_options', $this->_optioncache);
  538. }
  539. /**
  540. * Sets an option if it doesn't exist.
  541. */
  542. function add_option($option, $value)
  543. {
  544. $this->_populate_option_cache();
  545. if (!array_key_exists($option, $this->_optioncache) || $this->_optioncache[$option] === '')
  546. {
  547. $this->_optioncache[$option] = $value;
  548. update_option('lifestream_options', $this->_optioncache);
  549. }
  550. }
  551. function __($text, $params=null)
  552. {
  553. if (!is_array($params))
  554. {
  555. $params = func_get_args();
  556. $params = array_slice($params, 1);
  557. }
  558. return vsprintf(__($text, 'lifestream'), $params);
  559. }
  560. function _e($text, $params=null)
  561. {
  562. if (!is_array($params))
  563. {
  564. $params = func_get_args();
  565. $params = array_slice($params, 1);
  566. }
  567. echo vsprintf(__($text, 'lifestream'), $params);
  568. }
  569. function init()
  570. {
  571. global $wpdb;
  572. $offset = get_option('gmt_offset') * 3600;
  573. define('LIFESTREAM_DATE_OFFSET', $offset);
  574. load_plugin_textdomain('lifestream', false, 'lifestream/locales');
  575. $page = (isset($_GET['page']) ? $_GET['page'] : null);
  576. if (is_admin() && lifestream_str_startswith($page, 'lifestream'))
  577. {
  578. wp_enqueue_script('jquery');
  579. wp_enqueue_script('admin-forms');
  580. }
  581. add_feed('lifestream-feed', 'lifestream_rss_feed');
  582. $this->is_buddypress = (function_exists('bp_is_blog_page') ? true : false);
  583. // If this is an update we need to force reactivation
  584. if (LIFESTREAM_VERSION != $this->get_option('_version'))
  585. {
  586. $this->get_option('_version');
  587. $this->deactivate();
  588. $this->activate();
  589. }
  590. }
  591. function log_error($message, $feed_id=null)
  592. {
  593. global $wpdb;
  594. if ($feed_id)
  595. {
  596. $result = $wpdb->query($wpdb->prepare("INSERT INTO `".$wpdb->prefix."lifestream_error_log` (`feed_id`, `message`, `timestamp`) VALUES (%s, %s, %d)", $wpdb->escape($feed_id), $wpdb->escape($message), time()));
  597. }
  598. else
  599. {
  600. $result = $wpdb->query($wpdb->prepare("INSERT INTO `".$wpdb->prefix."lifestream_error_log` (`feed_id`, `message`, `timestamp`) VALUES (NULL, %s, %d)", $wpdb->escape($message), time()));
  601. }
  602. }
  603. function get_anchor_html($label, $href, $attrs=array())
  604. {
  605. // TODO: this might need to be optimized as string management is typically slow
  606. if ($this->get_option('links_new_windows') && empty($attrs['target']))
  607. {
  608. $attrs['target'] = '_blank';
  609. }
  610. $attrs['href'] = $href;
  611. $html = '<a';
  612. foreach ($attrs as $key=>$value)
  613. {
  614. $html .= ' '.$key.'="'.$value.'"';
  615. }
  616. $html .= '>'.$label.'</a>';
  617. return $html;
  618. }
  619. function get_digest_interval()
  620. {
  621. $interval = $this->get_option('digest_interval');
  622. switch ($interval)
  623. {
  624. case 'weekly':
  625. return 3600*24*7;
  626. case 'daily':
  627. return 3600*24;
  628. case 'hourly':
  629. return 3600;
  630. }
  631. }
  632. function get_cron_schedules($cron)
  633. {
  634. $interval = (int)$this->get_option('update_interval', 15);
  635. if (!($interval > 0)) $interval = 15;
  636. $cron['lifestream'] = array(
  637. 'interval' => $interval * 60,
  638. 'display' => $this->__('On Lifestream update')
  639. );
  640. $cron['lifestream_digest'] = array(
  641. 'interval' => (int)$this->get_digest_interval(),
  642. 'display' => $this->__('On Lifestream daily digest update')
  643. );
  644. return $cron;
  645. }
  646. function get_single_event($feed_type)
  647. {
  648. $events = $this->get_events(array('feed_types'=>array($feed_type), 'limit'=>1, 'break_groups'=>true));
  649. $event = $events[0];
  650. return $event;
  651. }
  652. function generate_unique_id()
  653. {
  654. return uniqid('ls_', true);
  655. }
  656. function digest_update()
  657. {
  658. global $wpdb;
  659. if ($this->get_option('daily_digest') != '1') return;
  660. $interval = $this->get_digest_interval();
  661. $options = array(
  662. 'id' => $this->generate_unique_id(),
  663. );
  664. $now = time();
  665. // If there was a previous digest, we show only events since it
  666. $from = $this->get_option('_last_digest');
  667. // Otherwise we show events within the interval period
  668. if (!$from) $from = $now - $interval;
  669. // make sure the post doesn't exist
  670. $results = $wpdb->get_results($wpdb->prepare("SELECT `post_id` FROM `".$wpdb->prefix."postmeta` WHERE `meta_key` = '_lifestream_digest_date' AND `meta_value` = %d LIMIT 0, 1", $now));
  671. if ($results) continue;
  672. $sql = $wpdb->prepare("SELECT t1.*, t2.`options` FROM `".$wpdb->prefix."lifestream_event_group` as `t1` INNER JOIN `".$wpdb->prefix."lifestream_feeds` as t2 ON t1.`feed_id` = t2.`id` WHERE t1.`timestamp` > %s AND t1.`timestamp` < %s ORDER BY t1.`timestamp` ASC", $from, $now);
  673. $results =& $wpdb->get_results($sql);
  674. $events = array();
  675. foreach ($results as &$result)
  676. {
  677. $events[] = new Lifestream_EventGroup($this, $result);
  678. }
  679. if (count($events))
  680. {
  681. ob_start();
  682. if (!include($this->get_theme_filepath('digest.inc.php'))) return;
  683. $content = sprintf($this->get_option('digest_body'), ob_get_clean(), date($this->get_option('day_format'), $now), count($events));
  684. $data = array(
  685. 'post_content' => $wpdb->escape($content),
  686. 'post_title' => $wpdb->escape(sprintf($this->get_option('digest_title'), date($this->get_option('day_format'), $now), date($this->get_option('hour_format'), $now))),
  687. 'post_date' => date('Y-m-d H:i:s', $now),
  688. 'post_category' => array($this->get_option('digest_category')),
  689. 'post_status' => 'publish',
  690. 'post_author' => $wpdb->escape($this->get_option('digest_author')),
  691. );
  692. $post_id = wp_insert_post($data);
  693. add_post_meta($post_id, '_lifestream_digest_date', $now, true);
  694. }
  695. $this->update_option('_last_digest', $now);
  696. }
  697. // page output
  698. function options_menu()
  699. {
  700. global $wpdb;
  701. wp_enqueue_script('postbox');
  702. if (function_exists('add_menu_page'))
  703. {
  704. $basename = basename(LIFESTREAM_PLUGIN_FILE);
  705. $results =& $wpdb->get_results("SELECT COUNT(*) as `count` FROM `".$wpdb->prefix."lifestream_error_log` WHERE has_viewed = 0");
  706. $errors = $results[0]->count;
  707. add_menu_page('Lifestream', 'Lifestream', 'edit_posts', $basename, array(&$this, 'options_page'));
  708. add_submenu_page($basename, $this->__('Lifestream Feeds'), $this->__('Feeds'), 'level_1', $basename, array(&$this, 'options_page'));
  709. add_submenu_page($basename, $this->__('Lifestream Events'), $this->__('Events'), 'edit_posts', 'lifestream-events.php', array(&$this, 'options_page'));
  710. add_submenu_page($basename, $this->__('Lifestream Settings'), $this->__('Settings'), 'manage_options', 'lifestream-settings.php', array(&$this, 'options_page'));
  711. add_submenu_page($basename, $this->__('Lifestream Change Log'), $this->__('Change Log'), 'edit_posts', 'lifestream-changelog.php', array(&$this, 'options_page'));
  712. add_submenu_page($basename, $this->__('Lifestream Errors'), $this->__('Errors (%d)', $errors), 'edit_posts', 'lifestream-errors.php', array(&$this, 'options_page'));
  713. add_submenu_page($basename, $this->__('Lifestream Maintenance'), $this->__('Maintenance / Debug', $errors), 'manage_options', 'lifestream-maintenance.php', array(&$this, 'options_page'));
  714. add_submenu_page($basename, $this->__('Lifestream Support Forums'), $this->__('Support Forums'), 'edit_posts', 'lifestream-forums.php', array(&$this, 'options_page'));
  715. }
  716. }
  717. function header()
  718. {
  719. echo '<script type="text/javascript" src="'.$this->path.'/lifestream.js"></script>';
  720. echo '<link rel="stylesheet" type="text/css" media="screen" href="'.$this->get_theme_media_url('lifestream.css').'"/>';
  721. }
  722. function options_page()
  723. {
  724. global $wpdb, $userdata;
  725. $wpdb->show_errors();
  726. $this->install();
  727. get_currentuserinfo();
  728. $date_format = sprintf('%s @ %s', $this->get_option('day_format'), $this->get_option('hour_format'));
  729. $basename = basename(LIFESTREAM_PLUGIN_FILE);
  730. $errors = array();
  731. $message = null;
  732. switch ($_GET['page'])
  733. {
  734. case 'lifestream-maintenance.php':
  735. if ($_POST['resetcron'])
  736. {
  737. $this->reschedule_cron();
  738. $message = $this->__('Cron timers have been reset.');
  739. }
  740. elseif ($_POST['restore'])
  741. {
  742. $this->restore_options();
  743. $message = $this->__('Default options have been restored.');
  744. }
  745. elseif ($_POST['restoredb'])
  746. {
  747. $this->restore_database();
  748. $message = $this->__('Default database has been restored.');
  749. }
  750. break;
  751. case 'lifestream-events.php':
  752. switch ((isset($_REQUEST['op']) ? strtolower($_REQUEST['op']) : null))
  753. {
  754. case 'delete':
  755. if (!($ids = $_REQUEST['id'])) break;
  756. if (!is_array($ids)) $ids = array($ids);
  757. foreach ($ids as $id)
  758. {
  759. $result =& $wpdb->get_results($wpdb->prepare("SELECT `id`, `feed_id`, `timestamp`, `owner_id` FROM `".$wpdb->prefix."lifestream_event` WHERE `id` = %d", $id));
  760. if (!$result)
  761. {
  762. $errors[] = $this->__('The selected feed was not found.');
  763. }
  764. elseif (!current_user_can('manage_options') && $result[0]->owner_id != $userdata->ID)
  765. {
  766. $errors[] = $this->__('You do not have permission to do that.');
  767. }
  768. else
  769. {
  770. $result =& $result[0];
  771. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_event` SET `visible` = 0 WHERE `id` = %d", $result->id));
  772. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_event_group` SET `visible` = 0 WHERE `event_id` = %d", $result->id));
  773. // Now we have to update the batch if it exists.
  774. $group =& $wpdb->get_results($wpdb->prepare("SELECT `id` FROM `".$wpdb->prefix."lifestream_event_group` WHERE `event_id` IS NULL AND DATE(FROM_UNIXTIME(`timestamp`)) = DATE(FROM_UNIXTIME(%d)) AND `feed_id` = %d LIMIT 0, 1", $result->timestamp, $result->feed_id));
  775. if (count($group) == 1)
  776. {
  777. $group =& $group[0];
  778. $results =& $wpdb->get_results($wpdb->prepare("SELECT `data`, `link` FROM `".$wpdb->prefix."lifestream_event` WHERE `feed_id` = %d AND `visible` = 1 AND DATE(FROM_UNIXTIME(`timestamp`)) = DATE(FROM_UNIXTIME(%d))", $result->feed_id, $result->timestamp));
  779. if (count($results))
  780. {
  781. $events = array();
  782. foreach ($results as &$result)
  783. {
  784. $result->data = unserialize($result->data);
  785. $result->data['link'] = $result->link;
  786. $events[] = $result->data;
  787. }
  788. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_event_group` SET `data` = %s, `total` = %d, `updated` = 1 WHERE `id` = %d", $wpdb->escape(serialize($events)), count($events), $group->id));
  789. }
  790. else
  791. {
  792. $wpdb->query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_event_group` WHERE `id` = %d", $group->id));
  793. }
  794. }
  795. else
  796. {
  797. $wpdb->query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_event_group` WHERE `event_id` = %d", $result->id));
  798. }
  799. }
  800. $message = $this->__('The selected events were hidden.');
  801. }
  802. break;
  803. }
  804. break;
  805. case 'lifestream-settings.php':
  806. if (!empty($_POST['save']))
  807. {
  808. foreach (array_keys($this->_options) as $value)
  809. {
  810. $this->update_option($value, (isset($_POST['lifestream_'.$value]) ? stripslashes($_POST['lifestream_'.$value]) : '0'));
  811. }
  812. // We need to make sure the cron runs now
  813. $this->reschedule_cron();
  814. }
  815. break;
  816. default:
  817. $feedmsgs = array();
  818. switch ((isset($_REQUEST['op']) ? strtolower($_REQUEST['op']) : null))
  819. {
  820. case 'refreshall':
  821. $results = $this->update_all($userdata->ID);
  822. foreach ($results as $id=>$result)
  823. {
  824. if (is_int($result)) $feedmsgs[$id] = $result;
  825. else $errors[] = $this->__('There was an error refreshing the selected feed: ID %s', $id);
  826. }
  827. $message = $this->__('All of your feeds have been refreshed.');
  828. break;
  829. case 'refresh':
  830. if (!$_REQUEST['id']) break;
  831. foreach ($_REQUEST['id'] as $id)
  832. {
  833. $result =& $wpdb->get_results($wpdb->prepare("SELECT * FROM `".$wpdb->prefix."lifestream_feeds` WHERE `id` = %d LIMIT 0, 1", $id));
  834. if (!$result)
  835. {
  836. $errors[] = $this->__('The selected feed was not found.');
  837. }
  838. elseif (!current_user_can('manage_options') && $result[0]->owner_id != $userdata->ID)
  839. {
  840. $errors[] = $this->__('You do not have permission to do that.');
  841. }
  842. else
  843. {
  844. $instance = Lifestream_Feed::construct_from_query_result($this, $result[0]);
  845. $msg_arr = $instance->refresh();
  846. if ($msg_arr[0] !== false)
  847. {
  848. $message = $this->__('The selected feeds and their events have been refreshed.');
  849. $feedmsgs[$instance->id] = $msg_arr[1];
  850. }
  851. else
  852. {
  853. $errors[] = $this->__('There was an error refreshing the selected feed: ID %s', $instance->id);
  854. }
  855. }
  856. }
  857. break;
  858. case 'pause':
  859. if (!$_REQUEST['id']) break;
  860. $ids = array();
  861. foreach ($_REQUEST['id'] as $id)
  862. {
  863. $ids[] = (int)$id;
  864. }
  865. if (!empty($ids))
  866. {
  867. if (current_user_can('manage_options'))
  868. {
  869. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_feeds` SET `active` = 0 WHERE `id` IN ('%s')", implode("','", $ids)));
  870. }
  871. else
  872. {
  873. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_feeds` SET `active` = 1 WHERE `id` IN ('%s') AND `owner_id` = %s", implode("','", $ids), $userdata->ID));
  874. }
  875. $message = $this->__('The selected feeds have been paused, and events will not be refreshed.');
  876. }
  877. break;
  878. case 'unpause':
  879. if (!$_REQUEST['id']) break;
  880. $ids = array();
  881. foreach ($_REQUEST['id'] as $id)
  882. {
  883. $ids[] = (int)$id;
  884. }
  885. if (!empty($ids))
  886. {
  887. if (current_user_can('manage_options'))
  888. {
  889. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_feeds` SET `active` = 1 WHERE `id` IN ('%s')", implode("','", $ids)));
  890. }
  891. else
  892. {
  893. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_feeds` SET `active` = 0 WHERE `id` IN ('%s') AND `owner_id` = %s", implode("','", $ids), $userdata->ID));
  894. }
  895. $message = $this->__('The selected feeds have been unpaused, and events will now be refreshed.');
  896. }
  897. break;
  898. case 'delete':
  899. if (!$_REQUEST['id']) break;
  900. foreach ($_REQUEST['id'] as $id)
  901. {
  902. $result =& $wpdb->get_results($wpdb->prepare("SELECT * FROM `".$wpdb->prefix."lifestream_feeds` WHERE `id` = %d LIMIT 0, 1", $id));
  903. if (!$result)
  904. {
  905. $errors[] = $this->__('The selected feed was not found.');
  906. }
  907. elseif (!current_user_can('manage_options') && $result[0]->owner_id != $userdata->ID)
  908. {
  909. $errors[] = $this->__('You do not have permission to do that.');
  910. }
  911. else
  912. {
  913. $instance = Lifestream_Feed::construct_from_query_result($this, $result[0]);
  914. $instance->delete();
  915. $message = $this->__('The selected feeds and all related events has been removed.');
  916. }
  917. }
  918. break;
  919. case 'edit':
  920. $result =& $wpdb->get_results($wpdb->prepare("SELECT * FROM `".$wpdb->prefix."lifestream_feeds` WHERE `id` = %d LIMIT 0, 1", $_GET['id']));
  921. if (!$result)
  922. {
  923. $errors[] = $this->__('The selected feed was not found.');
  924. }
  925. elseif (!current_user_can('manage_options') && $result[0]->owner_id != $userdata->ID)
  926. {
  927. $errors[] = $this->__('You do not have permission to do that.');
  928. }
  929. else
  930. {
  931. $instance = Lifestream_Feed::construct_from_query_result($this, $result[0]);
  932. $options = $instance->get_options();
  933. if (@$_POST['save'])
  934. {
  935. $values = array();
  936. foreach ($options as $option=>$option_meta)
  937. {
  938. if ($option_meta[1] && !$_POST[$option])
  939. {
  940. $errors[] = $option_meta[0].' is required.';
  941. }
  942. else
  943. {
  944. $values[$option] = stripslashes($_POST[$option]);
  945. }
  946. }
  947. if ($instance->get_constant('MUST_GROUP'))
  948. {
  949. $values['grouped'] = 1;
  950. }
  951. elseif ($instance->get_constant('CAN_GROUP'))
  952. {
  953. $values['grouped'] = $_POST['grouped'];
  954. }
  955. if ($instance->get_constant('HAS_EXCERPTS'))
  956. {
  957. $values['excerpt'] = $_POST['excerpt'];
  958. }
  959. $values['feed_label'] = $_POST['feed_label'];
  960. $values['icon_url'] = $_POST['icon_url'];
  961. $values['auto_icon'] = $_POST['auto_icon'];
  962. if ($_POST['owner'] != $instance->owner_id && current_user_can('manage_options'))
  963. {
  964. $usero = new WP_User($_POST['owner']);
  965. $owner = $usero->data;
  966. $instance->owner_id = $_POST['owner'];
  967. $instance->owner = $owner->display_name;
  968. }
  969. if (!count($errors))
  970. {
  971. $instance->options = $values;
  972. $instance->save();
  973. unset($_POST);
  974. }
  975. }
  976. elseif (@$_POST['truncate'])
  977. {
  978. $instance->truncate();
  979. $instance->refresh();
  980. }
  981. }
  982. break;
  983. case 'add':
  984. if ($_POST)
  985. {
  986. $class_name = $this->get_feed($_GET['feed']);
  987. if (!$class_name) break;
  988. $feed = new $class_name($this);
  989. $values = array();
  990. $options = $feed->get_options();
  991. foreach ($options as $option=>$option_meta)
  992. {
  993. if ($option_meta[1] && !$_POST[$option])
  994. {
  995. $errors[] = $option_meta[0].' is required.';
  996. }
  997. else
  998. {
  999. $values[$option] = stripslashes($_POST[$option]);
  1000. }
  1001. }
  1002. if ($feed->get_constant('MUST_GROUP'))
  1003. {
  1004. $values['grouped'] = 1;
  1005. }
  1006. elseif ($feed->get_constant('CAN_GROUP'))
  1007. {
  1008. $values['grouped'] = $_POST['grouped'];
  1009. }
  1010. if ($feed->get_constant('HAS_EXCERPTS'))
  1011. {
  1012. $values['excerpt'] = $_POST['excerpt'];
  1013. }
  1014. $values['feed_label'] = $_POST['feed_label'];
  1015. $values['icon_url'] = $_POST['icon_url'];
  1016. $values['auto_icon'] = @$_POST['auto_icon'];
  1017. if (current_user_can('manage_options'))
  1018. {
  1019. $feed->owner_id = $_POST['owner'];
  1020. $usero = new WP_User($feed->owner_id);
  1021. $owner = $usero->data;
  1022. $feed->owner = $owner->display_name;
  1023. }
  1024. else
  1025. {
  1026. $feed->owner_id = $userdata->ID;
  1027. $feed->owner = $userdata->display_name;
  1028. }
  1029. $feed->options = $values;
  1030. if (!count($errors))
  1031. {
  1032. if (!($error = $feed->test()))
  1033. {
  1034. $result = $feed->save();
  1035. if ($result !== false)
  1036. {
  1037. unset($_POST);
  1038. unset($_REQUEST['op']);
  1039. $msg_arr = $feed->refresh(null, true);
  1040. if ($msg_arr[0] !== false)
  1041. {
  1042. $message = $this->__('A new %s feed was added to your Lifestream.', $feed->get_constant('NAME'));
  1043. $feedmsgs[$feed->id] = $msg_arr[1];
  1044. }
  1045. }
  1046. }
  1047. else
  1048. {
  1049. $errors[] = $error;
  1050. }
  1051. }
  1052. }
  1053. break;
  1054. }
  1055. break;
  1056. }
  1057. $lifestream = &$this;
  1058. ob_start();
  1059. ?>
  1060. <style type="text/css">
  1061. .feedlist { margin: 0; padding: 0; }
  1062. .feedlist li { list-style: none; display: inline; }
  1063. .feedlist li a { float: left; display: block; padding: 2px; margin: 1px; width: 23%; text-decoration: none; }
  1064. .feedlist li a:hover { background-color: #e9e9e9; }
  1065. .success { color: #397D33; background-color: #D1FBCA; }
  1066. .error { border-color: #E25F53; color: #E25F53; }
  1067. td.icon { padding: 7px 0 9px 10px; }
  1068. </style>
  1069. <br />
  1070. <?php
  1071. if (count($errors)) { ?>
  1072. <div id="message" class="error"><p><strong><?php $this->_e('There were errors with your request:') ?></strong></p><ul>
  1073. <?php foreach ($errors as $error) { ?>
  1074. <li><?php echo nl2br(Lifestream_Feed::parse_urls(htmlspecialchars($error))); ?></li>
  1075. <?php } ?>
  1076. </ul></div>
  1077. <?php } elseif ($message) { ?>
  1078. <div id="message" class="updated fade"><p><strong><?php echo $message; ?></strong></p></div>
  1079. <?php } ?>
  1080. <div class="wrap">
  1081. <?php
  1082. switch ($_GET['page'])
  1083. {
  1084. case 'lifestream-errors.php':
  1085. $page = (!empty($_GET['paged']) ? $_GET['paged'] : 1);
  1086. switch ((isset($_REQUEST['op']) ? strtolower($_REQUEST['op']) : null))
  1087. {
  1088. case 'clear':
  1089. $wpdb->query("DELETE FROM `".$wpdb->prefix."lifestream_error_log`");
  1090. break;
  1091. }
  1092. $start = ($page-1)*LIFESTREAM_ERRORS_PER_PAGE;
  1093. $end = $page*LIFESTREAM_ERRORS_PER_PAGE;
  1094. $wpdb->query("UPDATE `".$wpdb->prefix."lifestream_error_log` SET has_viewed = 1");
  1095. $results =& $wpdb->get_results("SELECT COUNT(*) as `count` FROM `".$wpdb->prefix."lifestream_error_log`");
  1096. $number_of_pages = ceil($results[0]->count/LIFESTREAM_EVENTS_PER_PAGE);
  1097. $results =& $wpdb->get_results($wpdb->prepare("SELECT t1.*, t2.`feed`, t2.`options` FROM `".$wpdb->prefix."lifestream_error_log` as t1 LEFT JOIN `".$wpdb->prefix."lifestream_feeds` as t2 ON t1.`feed_id` = t2.`id` ORDER BY t1.`timestamp` DESC LIMIT %d, %d", $start, $end));
  1098. include(LIFESTREAM_PATH . '/pages/errors.inc.php');
  1099. break;
  1100. case 'lifestream-maintenance.php':
  1101. include(LIFESTREAM_PATH . '/pages/maintenance.inc.php');
  1102. break;
  1103. case 'lifestream-changelog.php':
  1104. include(LIFESTREAM_PATH . '/pages/changelog.inc.php');
  1105. break;
  1106. case 'lifestream-forums.php':
  1107. include(LIFESTREAM_PATH . '/pages/forums.inc.php');
  1108. break;
  1109. case 'lifestream-settings.php':
  1110. $lifestream_digest_intervals = array(
  1111. 'weekly' => $this->__('Weekly'),
  1112. 'daily' => $this->__('Daily'),
  1113. 'hourly' => $this->__('Hourly'),
  1114. );
  1115. include(LIFESTREAM_PATH . '/pages/settings.inc.php');
  1116. break;
  1117. case 'lifestream-events.php':
  1118. $page = (!empty($_GET['paged']) ? $_GET['paged'] : 1);
  1119. $start = ($page-1)*LIFESTREAM_EVENTS_PER_PAGE;
  1120. $end = $page*LIFESTREAM_EVENTS_PER_PAGE;
  1121. if (!current_user_can('manage_options'))
  1122. {
  1123. $rows =& $wpdb->get_row($wpdb->prepare("SELECT COUNT(*) as `count` FROM `".$wpdb->prefix."lifestream_event` WHERE `owner_id` = %d", $userdata->ID));
  1124. $number_of_pages = ceil($rows->count/LIFESTREAM_EVENTS_PER_PAGE);
  1125. $rows =& $wpdb->get_results($wpdb->prepare("SELECT t1.*, t2.`feed`, t2.`options` FROM `".$wpdb->prefix."lifestream_event` as t1 JOIN `".$wpdb->prefix."lifestream_feeds` as t2 ON t1.`feed_id` = t2.`id` WHERE t1.`owner_id` = %d ORDER BY t1.`timestamp` DESC LIMIT %d, %d", $userdata->ID, $start, $end));
  1126. }
  1127. else
  1128. {
  1129. $rows =& $wpdb->get_row("SELECT COUNT(*) as `count` FROM `".$wpdb->prefix."lifestream_event`");
  1130. $number_of_pages = ceil($rows->count/LIFESTREAM_EVENTS_PER_PAGE);
  1131. $rows =& $wpdb->get_results($wpdb->prepare("SELECT t1.*, t2.`feed`, t2.`options` FROM `".$wpdb->prefix."lifestream_event` as t1 JOIN `".$wpdb->prefix."lifestream_feeds` as t2 ON t1.`feed_id` = t2.`id` ORDER BY t1.`timestamp` DESC LIMIT %d, %d", $start, $end));
  1132. }
  1133. $results = array();
  1134. foreach ($rows as $result)
  1135. {
  1136. $results[] = new Lifestream_Event($lifestream, $result);
  1137. }
  1138. unset($rows);
  1139. include(LIFESTREAM_PATH . '/pages/events.inc.php');
  1140. break;
  1141. default:
  1142. switch ((isset($_REQUEST['op']) ? strtolower($_REQUEST['op']) : null))
  1143. {
  1144. case 'edit':
  1145. include(LIFESTREAM_PATH . '/pages/edit-feed.inc.php');
  1146. break;
  1147. case 'add':
  1148. $identifier = $_GET['feed'];
  1149. $class_name = $this->get_feed($identifier);
  1150. if (!$class_name) break;
  1151. $feed = new $class_name($this);
  1152. $options = $feed->get_options();
  1153. include(LIFESTREAM_PATH . '/pages/add-feed.inc.php');
  1154. break;
  1155. default:
  1156. $page = (!empty($_GET['paged']) ? $_GET['paged'] : 1);
  1157. $start = ($page-1)*LIFESTREAM_FEEDS_PER_PAGE;
  1158. $end = $page*LIFESTREAM_FEEDS_PER_PAGE;
  1159. if (!current_user_can('manage_options'))
  1160. {
  1161. $rows =& $wpdb->get_row($wpdb->prepare("SELECT COUNT(*) as `count` FROM `".$wpdb->prefix."lifestream_feeds` WHERE `owner_id` = %d", $userdata->ID));
  1162. $number_of_pages = ceil($rows->count/LIFESTREAM_FEEDS_PER_PAGE);
  1163. $rows =& $wpdb->get_results($wpdb->prepare("SELECT t1.*, (SELECT COUNT(1) FROM `".$wpdb->prefix."lifestream_event` WHERE `feed_id` = t1.`id`) as `events` FROM `".$wpdb->prefix."lifestream_feeds` as t1 WHERE t1.`owner_id` = %d ORDER BY `id` LIMIT %d, %d", $userdata->ID, $start, $end));
  1164. }
  1165. else
  1166. {
  1167. $rows =& $wpdb->get_row("SELECT COUNT(*) as `count` FROM `".$wpdb->prefix."lifestream_feeds`");
  1168. $number_of_pages = ceil($rows->count/LIFESTREAM_FEEDS_PER_PAGE);
  1169. $rows =& $wpdb->get_results($wpdb->prepare("SELECT t1.*, (SELECT COUNT(1) FROM `".$wpdb->prefix."lifestream_event` WHERE `feed_id` = t1.`id`) as `events` FROM `".$wpdb->prefix."lifestream_feeds` as t1 ORDER BY `id` LIMIT %d, %d", $start, $end));
  1170. }
  1171. $results = array();
  1172. foreach ($rows as $result)
  1173. {
  1174. $results[] = Lifestream_Feed::construct_from_query_result($this, $result);
  1175. }
  1176. if ($results !== false)
  1177. {
  1178. include(LIFESTREAM_PATH . '/pages/feeds.inc.php');
  1179. }
  1180. break;
  1181. }
  1182. break;
  1183. }
  1184. ?>
  1185. </div>
  1186. <?php
  1187. ob_end_flush();
  1188. }
  1189. /**
  1190. * Cleans up old entries based on the `truncate_interval` setting.
  1191. */
  1192. function cleanup_history()
  1193. {
  1194. $int = $this->get_option('truncate_interval');
  1195. if (!(int)$int) return;
  1196. // the value is in days
  1197. $ts = time()-(int)$int*3600*24;
  1198. $result = $wpdb->query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_event` WHERE `timestamp` < %s", $wpdb->escape($ts)));
  1199. $result = $wpdb->query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_event_group` WHERE `timestamp` < %s", $wpdb->escape($ts)));
  1200. $result = $wpdb->query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_error_log` WHERE `timestamp` < %s", $wpdb->escape($ts)));
  1201. }
  1202. /**
  1203. * Attempts to update all feeds
  1204. */
  1205. function update($user_id=null)
  1206. {
  1207. $event_arr = $this->update_all($user_id);
  1208. $events = 0;
  1209. foreach ($event_arr as $instance=>$result)
  1210. {
  1211. if (is_int($result)) $events += $result;
  1212. }
  1213. return $events;
  1214. }
  1215. function update_all($user_id=null)
  1216. {
  1217. // $user_id is not implemented yet
  1218. global $wpdb;
  1219. $this->update_option('_last_update', time());
  1220. $events = array();
  1221. $results =& $wpdb->get_results("SELECT * FROM `".$wpdb->prefix."lifestream_feeds` WHERE `active` = 1");
  1222. foreach ($results as $result)
  1223. {
  1224. $instance = Lifestream_Feed::construct_from_query_result($this, $result);
  1225. try
  1226. {
  1227. $feed_msg = $instance->refresh();
  1228. $events[$instance->id] = $feed_msg[1];
  1229. }
  1230. catch (Lifestream_FeedFetchError $ex)
  1231. {
  1232. $this->log_error($ex, $instance->id);
  1233. $events[$instance->id] = $ex;
  1234. }
  1235. }
  1236. return $events;
  1237. }
  1238. /**
  1239. * Registers a feed class with Lifestream.
  1240. * @param $class_name {Class} Should extend Lifestream_Extension.
  1241. */
  1242. function register_feed($class_name)
  1243. {
  1244. $this->feeds[lifestream_get_class_constant($class_name, 'ID')] = $class_name;
  1245. // this may be the ugliest thing ever written in PHP, thank you developers!
  1246. $rcl = new ReflectionClass($class_name);
  1247. $this->paths[$class_name] = dirname($rcl->getFileName());
  1248. unset($rcl);
  1249. }
  1250. function get_feed($class_name)
  1251. {
  1252. return $this->feeds[$class_name];
  1253. }
  1254. /**
  1255. * Similar to file_get_contents but will use curl by default.
  1256. */
  1257. function file_get_contents($url)
  1258. {
  1259. $handler = $this->get_option('url_handler');
  1260. $use_fsock = true;
  1261. if (($handler == 'auto' && function_exists('curl_init')) || $handler == 'curl')
  1262. {
  1263. $use_fsock = false;
  1264. }
  1265. $file = new SimplePie_File($url, 10, 5, null, SIMPLEPIE_USERAGENT, $use_fsock);
  1266. if (!$file->success)
  1267. {
  1268. throw new Lifestream_FeedFetchError('Failed to open url: '.$url .' ('.$file->error.')');
  1269. }
  1270. return $file->body;
  1271. }
  1272. /*
  1273. * This is a wrapper function which initiates the callback for the custom tag embedding.
  1274. */
  1275. function embed_callback($content)
  1276. {
  1277. return preg_replace_callback("|\[lifestream(?:\s+([^\]]+))?\]|i", array(&$this, 'embed_handler'), $content);
  1278. return preg_replace_callback("|<\[]lifestream(?:\s+([^>\]+]))?/?[>\]]|i", array(&$this, 'embed_handler'), $content);
  1279. }
  1280. /*
  1281. * This function handles the real meat by handing off the work to helper functions.
  1282. */
  1283. function embed_handler($matches)
  1284. {
  1285. $args = array();
  1286. if (count($matches) > 1)
  1287. {
  1288. preg_match_all("|(?:([a-z_]+)=[\"']?([a-z0-9_-\s]+)[\"']?)\s*|i", $matches[1], $options);
  1289. for ($i=0; $i<count($options[1]); $i++)
  1290. {
  1291. if ($options[$i]) $args[$options[1][$i]] = $options[2][$i];
  1292. }
  1293. }
  1294. ob_start();
  1295. if (!empty($args['feed_ids'])) $args['feed_ids'] = explode(',', $args['feed_ids']);
  1296. if (!empty($args['user_ids'])) $args['user_ids'] = explode(',', $args['user_ids']);
  1297. if (!empty($args['feed_types'])) $args['feed_types'] = explode(',', $args['feed_types']);
  1298. lifestream($args);
  1299. return ob_get_clean();
  1300. }
  1301. /**
  1302. * Returns the duration from now until timestamp.
  1303. * @param $timestamp {Integer}
  1304. * @param $granularity {Integer}
  1305. * @param $format {String} Date format.
  1306. * @return {String}
  1307. */
  1308. function timesince($timestamp, $granularity=1, $format='Y-m-d H:i:s')
  1309. {
  1310. $difference = time() - $timestamp;
  1311. if ($difference < 0) return 'just now';
  1312. elseif ($difference < 86400*2)
  1313. {
  1314. return $this->duration($difference, $granularity) . ' ago';
  1315. }
  1316. else
  1317. {
  1318. return date($this->get_option('day_format'), $timestamp);
  1319. }
  1320. }
  1321. /**
  1322. * Returns the duration from a difference.
  1323. * @param $difference {Integer}
  1324. * @param $granularity {Integer}
  1325. * @return {String}
  1326. */
  1327. function duration($difference, $granularity=2)
  1328. {
  1329. { // if difference is over 10 days show normal time form
  1330. $periods = array(
  1331. $this->__('w') => 604800,
  1332. $this->__('d') => 86400,
  1333. $this->__('h') => 3600,
  1334. $this->__('m') => 60,
  1335. $this->__('s') => 1
  1336. );
  1337. $output = '';
  1338. foreach ($periods as $key => $value)
  1339. {
  1340. if ($difference >= $value)
  1341. {
  1342. $time = round($difference / $value);
  1343. $difference %= $value;
  1344. $output .= ($output ? ' ' : '').$time.$key;
  1345. //$output .= (($time > 1 && ($key == 'week' || $key == 'day')) ? $key.'s' : $key);
  1346. $granularity--;
  1347. }
  1348. if ($granularity == 0) break;
  1349. }
  1350. return ($output ? $output : '0 seconds');
  1351. }
  1352. }
  1353. function get_cron_task_description($name)
  1354. {
  1355. switch ($name)
  1356. {
  1357. case 'lifestream_cleanup':
  1358. return 'Cleans up old events and error messages.';
  1359. break;
  1360. case 'lifestream_cron':
  1361. return 'Updates all active feeds.';
  1362. break;
  1363. case 'lifestream_digest_cron':
  1364. return 'Creates a daily digest post if enabled.';
  1365. break;
  1366. }
  1367. }
  1368. function restore_options()
  1369. {
  1370. // default options and their values
  1371. foreach ($this->_options as $key=>$value)
  1372. {
  1373. $this->update_option($key, $value);
  1374. }
  1375. $this->update_option('extension_dir', WP_CONTENT_DIR.'/wp-lifestream/extensions/');
  1376. $this->update_option('theme_dir', WP_CONTENT_DIR.'/wp-lifestream/themes/');
  1377. $this->update_option('icon_dir', WP_CONTENT_DIR.'/wp-lifestream/icons/');
  1378. }
  1379. function restore_database()
  1380. {
  1381. global $wpdb;
  1382. $this->safe_query("DROP TABLE `".$wpdb->prefix."lifestream_event`;");
  1383. $this->safe_query("DROP TABLE `".$wpdb->prefix."lifestream_event_group`;");
  1384. $this->safe_query("DROP TABLE `".$wpdb->prefix."lifestream_feeds`;");
  1385. $this->safe_query("DROP TABLE `".$wpdb->prefix."lifestream_error_log`;");
  1386. $this->install_database();
  1387. }
  1388. function reschedule_cron()
  1389. {
  1390. wp_clear_scheduled_hook('lifestream_cron');
  1391. wp_clear_scheduled_hook('lifestream_cleanup');
  1392. wp_clear_scheduled_hook('lifestream_digest_cron');
  1393. wp_schedule_event(time()+60, 'daily', 'lifestream_cleanup');
  1394. // First lifestream cron should not happen instantly, incase we need to reschedule
  1395. wp_schedule_event(time()+60, 'lifestream', 'lifestream_cron');
  1396. // We have to calculate the time for the first digest
  1397. $digest_time = $this->get_option('digest_time');
  1398. $digest_interval = $this->get_option('digest_interval');
  1399. $time = time();
  1400. if ($digest_interval == 'hourly')
  1401. {
  1402. // Start at the next hour
  1403. $time = strtotime(date('Y-m-d H:00:00', strtotime('+1 hour', $time)));
  1404. }
  1405. else
  1406. {
  1407. // If time has already passed for today, set it for tomorrow
  1408. if (date('H') > $digest_time)
  1409. {
  1410. $time = strtotime('+1 day', $time);
  1411. }
  1412. else
  1413. {
  1414. $time = strtotime(date('Y-m-d '.$digest_time.':00:00', $time));
  1415. }
  1416. }
  1417. wp_schedule_event($time, 'lifestream_digest', 'lifestream_digest_cron');
  1418. }
  1419. function deactivate()
  1420. {
  1421. wp_clear_scheduled_hook('lifestream_cron');
  1422. wp_clear_scheduled_hook('lifestream_cleanup');
  1423. wp_clear_scheduled_hook('lifestream_digest_cron');
  1424. }
  1425. /**
  1426. * Initializes the plug-in upon activation.
  1427. */
  1428. function activate()
  1429. {
  1430. global $wpdb, $userdata;
  1431. get_currentuserinfo();
  1432. // Options/database install
  1433. $this->install();
  1434. // Add a feed for this blog
  1435. $results =& $wpdb->get_results("SELECT COUNT(*) as `count` FROM `".$wpdb->prefix."lifestream_feeds`");
  1436. if (!$results[0]->count)
  1437. {
  1438. $rss_url = get_bloginfo('rss2_url');
  1439. $options = array('url' => $rss_url);
  1440. $feed = new Lifestream_BlogFeed($this, $options);
  1441. $feed->owner = $userdata->display_name;
  1442. $feed->owner_id = $userdata->ID;
  1443. $feed->save(false);
  1444. }
  1445. // Cron job for the update
  1446. $this->reschedule_cron();
  1447. }
  1448. function credits()
  1449. {
  1450. return 'Powered by <a href="http://www.enthropia.com/labs/wp-lifestream/">Lifestream</a>.';
  1451. }
  1452. /**
  1453. * Adds/updates the options on plug-in activation.
  1454. */
  1455. function install($allow_database_install=true)
  1456. {
  1457. $version = (string)$this->get_option('_version', 0);
  1458. if ($allow_database_install)
  1459. {
  1460. $this->install_database($version);
  1461. }
  1462. if (version_compare($version, '0.95', '<'))
  1463. {
  1464. foreach ($this->_options as $key=>$value)
  1465. {
  1466. $ovalue = get_option('lifestream_' . $key);
  1467. if (!$ovalue)
  1468. {
  1469. $value = $value;
  1470. }
  1471. else
  1472. {
  1473. delete_option('lifestream_' . $key);
  1474. }
  1475. $this->add_option($key, $value);
  1476. }
  1477. }
  1478. // default options and their values
  1479. foreach ($this->_options as $key=>$value)
  1480. {
  1481. $this->add_option($key, $value);
  1482. }
  1483. // because these are based off of the WP_CONTENT_DIR they cannot be included in $_options
  1484. $this->add_option('extension_dir', WP_CONTENT_DIR.'/wp-lifestream/extensions/');
  1485. $this->add_option('theme_dir', WP_CONTENT_DIR.'/wp-lifestream/themes/');
  1486. $this->add_option('icon_dir', WP_CONTENT_DIR.'/wp-lifestream/icons/');
  1487. if (version_compare($version, LIFESTREAM_VERSION, '=')) return;
  1488. $this->update_option('_version', LIFESTREAM_VERSION);
  1489. }
  1490. /**
  1491. * Executes a MySQL query with exception handling.
  1492. */
  1493. function safe_query($sql)
  1494. {
  1495. global $wpdb;
  1496. $result = $wpdb->query($sql);
  1497. if ($result === false)
  1498. {
  1499. if ($wpdb->error)
  1500. {
  1501. $reason = $wpdb->error->get_error_message();
  1502. }
  1503. else
  1504. {
  1505. $reason = $this->__('Unknown SQL Error');
  1506. }
  1507. $this->log_error($reason);
  1508. throw new Lifestream_Error($reason);
  1509. }
  1510. return $result;
  1511. }
  1512. /**
  1513. * Initializes the database if it's not already present.
  1514. */
  1515. function install_database($version=0)
  1516. {
  1517. global $wpdb, $userdata;
  1518. get_currentuserinfo();
  1519. $this->safe_query("CREATE TABLE IF NOT EXISTS `".$wpdb->prefix."lifestream_event` (
  1520. `id` int(11) NOT NULL auto_increment,
  1521. `feed_id` int(11) NOT NULL,
  1522. `feed` varchar(32) NOT NULL,
  1523. `link` varchar(200) NOT NULL,
  1524. `data` blob NOT NULL,
  1525. `visible` tinyint(1) default 1 NOT NULL,
  1526. `timestamp` int(11) NOT NULL,
  1527. `version` int(11) default 0 NOT NULL,
  1528. `key` char(16) NOT NULL,
  1529. `owner` varchar(128) NOT NULL,
  1530. `owner_id` int(11) NOT NULL,
  1531. PRIMARY KEY (`id`),
  1532. INDEX `feed` (`feed`),
  1533. UNIQUE `feed_id` (`feed_id`, `key`, `owner_id`, `link`)
  1534. ) ENGINE=MyISAM;");
  1535. $this->safe_query("CREATE TABLE IF NOT EXISTS `".$wpdb->prefix."lifestream_event_group` (
  1536. `id` int(11) NOT NULL auto_increment,
  1537. `feed_id` int(11) NOT NULL,
  1538. `event_id` int(11) NULL,
  1539. `feed` varchar(32) NOT NULL,
  1540. `data` blob NOT NULL,
  1541. `total` int(11) default 1 NOT NULL,
  1542. `updated` tinyint(1) default 0 NOT NULL,
  1543. `visible` tinyint(1) default 1 NOT NULL,
  1544. `timestamp` int(11) NOT NULL,
  1545. `version` int(11) default 0 NOT NULL,
  1546. `key` char(16) NOT NULL,
  1547. `owner` varchar(128) NOT NULL,
  1548. `owner_id` int(11) NOT NULL,
  1549. PRIMARY KEY (`id`),
  1550. INDEX `feed` (`feed`),
  1551. INDEX `feed_id` (`feed_id`, `key`, `owner_id`, `timestamp`)
  1552. ) ENGINE=MyISAM;");
  1553. $this->safe_query("CREATE TABLE IF NOT EXISTS `".$wpdb->prefix."lifestream_feeds` (
  1554. `id` int(11) NOT NULL auto_increment,
  1555. `feed` varchar(32) NOT NULL,
  1556. `options` text default NULL,
  1557. `timestamp` int(11) NOT NULL,
  1558. `active` tinyint(1) default 1 NOT NULL,
  1559. `owner` varchar(128) NOT NULL,
  1560. `owner_id` int(11) NOT NULL,
  1561. `version` int(11) default 0 NOT NULL,
  1562. INDEX `owner_id` (`owner_id`),
  1563. PRIMARY KEY (`id`)
  1564. ) ENGINE=MyISAM;");
  1565. $this->safe_query("CREATE TABLE IF NOT EXISTS `".$wpdb->prefix."lifestream_error_log` (
  1566. `id` int(11) NOT NULL auto_increment,
  1567. `message` varchar(255) NOT NULL,
  1568. `trace` text NULL,
  1569. `feed_id` int(11) NULL,
  1570. `timestamp` int(11) NOT NULL,
  1571. `has_viewed` tinyint(1) default 0 NOT NULL,
  1572. INDEX `feed_id` (`feed_id`, `has_viewed`),
  1573. INDEX `has_viewed` (`has_viewed`),
  1574. PRIMARY KEY (`id`)
  1575. ) ENGINE=MyISAM;");
  1576. if (!$version) return;
  1577. // URGENT TODO: we need to solve alters when the column already exists due to WP issues
  1578. if (version_compare($version, '0.5', '<'))
  1579. {
  1580. // Old wp-cron built-in stuff
  1581. wp_clear_scheduled_hook('Lifestream_Hourly');
  1582. // Upgrade them to version 0.5
  1583. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_event_group` ADD `version` INT(11) NOT NULL DEFAULT '0' AFTER `timestamp`, ADD `key` CHAR( 16 ) NOT NULL AFTER `version`;");
  1584. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_event` ADD `version` INT(11) NOT NULL DEFAULT '0' AFTER `timestamp`, ADD `key` CHAR( 16 ) NOT NULL AFTER `version`;");
  1585. }
  1586. if (version_compare($version, '0.6', '<'))
  1587. {
  1588. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_event_group` ADD `owner` VARCHAR(128) NOT NULL AFTER `key`, ADD `owner_id` INT(11) NOT NULL AFTER `owner`;");
  1589. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_event` ADD `owner` VARCHAR(128) NOT NULL AFTER `key`, ADD `owner_id` INT(11) NOT NULL AFTER `owner`;");
  1590. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_feeds` ADD `owner` VARCHAR(128) NOT NULL AFTER `timestamp`, ADD `owner_id` INT(11) NOT NULL AFTER `owner`;");
  1591. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_event` DROP INDEX `feed_id`, ADD UNIQUE `feed_id` (`feed_id` , `key` , `owner_id` , `link` );");
  1592. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_event_group` DROP INDEX `feed_id`, ADD INDEX `feed_id` (`feed_id` , `key` , `timestamp` , `owner_id`);");
  1593. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_feeds` ADD INDEX `owner_id` (`owner_id`);");
  1594. $this->safe_query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_feeds` SET `owner` = %s, `owner_id` = %d", $userdata->display_name, $userdata->ID));
  1595. $this->safe_query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_event` SET `owner` = %s, `owner_id` = %d", $userdata->display_name, $userdata->ID));
  1596. $this->safe_query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_event_group` SET `owner` = %s, `owner_id` = %d", $userdata->display_name, $userdata->ID));
  1597. }
  1598. if (version_compare($version, '0.81', '<'))
  1599. {
  1600. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_event` ADD `feed` VARCHAR(32) NOT NULL AFTER `feed_id`");
  1601. $this->safe_query("UPDATE IGNORE `".$wpdb->prefix."lifestream_event` as t1 set t1.`feed` = (SELECT t2.`feed` FROM `".$wpdb->prefix."lifestream_feeds` as t2 WHERE t1.`feed_id` = t2.`id`)");
  1602. }
  1603. if (version_compare($version, '0.84', '<'))
  1604. {
  1605. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_event` ADD INDEX ( `feed` )");
  1606. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_event_group` ADD INDEX ( `feed` )");
  1607. }
  1608. if (version_compare($version, '0.90', '<'))
  1609. {
  1610. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_feeds` ADD `version` int(11) default 0 NOT NULL AFTER `owner_id`");
  1611. }
  1612. if (version_compare($version, '0.99.6.0', '<'))
  1613. {
  1614. $wpdb->query("ALTER IGNORE TABLE `".$wpdb->prefix."lifestream_feeds` ADD `active` tinyint(1) default 1 NOT NULL AFTER `timestamp`");
  1615. }
  1616. if (version_compare($version, '0.99.9.4', '<'))
  1617. {
  1618. $wpdb->query("UPDATE `".$wpdb->prefix."lifestream_feeds` SET `active` = 1");
  1619. }
  1620. }
  1621. function get_page_from_request()
  1622. {
  1623. return (!empty($_GET[$this->paging_key]) ? $_GET[$this->paging_key] : 1);
  1624. }
  1625. function get_next_page_url($page=null)
  1626. {
  1627. if (!$page) $page = $this->get_page_from_request();
  1628. if (strpos($_SERVER['QUERY_STRING'], '?') !== false) {
  1629. $url = str_replace('&'.$this->paging_key.'='.$page, '', $_SERVER['QUERY_STRING']);
  1630. return $url.'&'.$this->paging_key.'='.($page+1);
  1631. }
  1632. return '?'.$this->paging_key.'='.($page+1);
  1633. }
  1634. function get_previous_page_url($page=null)
  1635. {
  1636. if (!$page) $page = $this->get_page_from_request();
  1637. if (strpos($_SERVER['QUERY_STRING'], '?') !== false) {
  1638. $url = str_replace('&'.$this->paging_key.'='.$page, '', $_SERVER['QUERY_STRING']);
  1639. return $url.'&'.$this->paging_key.'='.($page-1);
  1640. }
  1641. return '?'.$this->paging_key.'='.($page-1);
  1642. }
  1643. /**
  1644. * Gets recent events from the lifestream.
  1645. * @param {Array} $_ An array of keyword args.
  1646. */
  1647. function get_events($_=array())
  1648. {
  1649. global $wpdb;
  1650. $defaults = array(
  1651. // number of events
  1652. 'event_ids' => array(),
  1653. 'limit' => $this->get_option('number_of_items'),
  1654. // offset of events (e.g. pagination)
  1655. 'offset' => 0,
  1656. // array of feed ids
  1657. 'feed_ids' => array(),
  1658. // array of user ids
  1659. 'user_ids' => array(),
  1660. // array of feed type identifiers
  1661. 'feed_types' => array(),
  1662. // interval for date cutoff (see mysql INTERVAL)
  1663. 'date_interval' => $this->get_option('date_interval'),
  1664. // start date of events
  1665. 'start_date' => -1,
  1666. // end date
  1667. 'end_date' => -1,
  1668. // minimum number of events in group
  1669. 'event_total_min' => -1,
  1670. // maximum
  1671. 'event_total_max' => -1,
  1672. // break groups into single events
  1673. 'break_groups' => false,
  1674. );
  1675. $_ = array_merge($defaults, $_);
  1676. # If any arguments are invalid we bail out
  1677. // Old-style
  1678. if (!empty($_['number_of_results'])) $_['limit'] = $_['number_of_results'];
  1679. if (!((int)$_['limit'] > 0)) return false;
  1680. if (!((int)$_['offset'] >= 0)) return false;
  1681. if (!preg_match('/[\d]+ (month|day|year|hour|second|microsecond|week|quarter)s?/', $_['date_interval'])) $_['date_interval'] = -1;
  1682. else $_['date_interval'] = rtrim($_['date_interval'], 's');
  1683. $_['feed_ids'] = (array)$_['feed_ids'];
  1684. $_['event_ids'] = (array)$_['event_ids'];
  1685. $_['user_ids'] = (array)$_['user_ids'];
  1686. $_['feed_types'] = (array)$_['feed_types'];
  1687. $where = array('t1.`visible` = 1');
  1688. if (count($_['feed_ids']))
  1689. {
  1690. foreach ($_['feed_ids'] as $key=>$value)
  1691. {
  1692. $_['feed_ids'][$key] = $wpdb->escape($value);
  1693. }
  1694. $where[] = 't1.`feed_id` IN ('.implode(', ', $_['feed_ids']).')';
  1695. }
  1696. elseif (count($_['event_ids']))
  1697. {
  1698. foreach ($_['event_ids'] as $key=>$value)
  1699. {
  1700. $_['event_ids'][$key] = $wpdb->escape($value);
  1701. }
  1702. $where[] = 't1.`id` IN ('.implode(', ', $_['event_ids']).')';
  1703. }
  1704. elseif (count($_['feed_types']))
  1705. {
  1706. foreach ($_['feed_types'] as $key=>$value)
  1707. {
  1708. $_['feed_types'][$key] = $wpdb->escape($value);
  1709. }
  1710. $where[] = 't1.`feed` IN ("'.implode('", "', $_['feed_types']).'")';
  1711. }
  1712. if (count($_['user_ids']))
  1713. {
  1714. foreach ($_['user_ids'] as $key=>$value)
  1715. {
  1716. $_['user_ids'][$key] = $wpdb->escape($value);
  1717. }
  1718. $where[] = 't1.`owner_id` IN ('.implode(', ', $_['user_ids']).')';
  1719. }
  1720. if ($_['event_total_max'] > -1)
  1721. {
  1722. $where[] = sprintf('t1.`total` <= %d', $_['event_total_max']);
  1723. }
  1724. if ($_['event_total_min'] > -1)
  1725. {
  1726. $where[] = sprintf('t1.`total` >= %d', $_['event_total_min']);
  1727. }
  1728. if ($_['date_interval'] !== -1)
  1729. {
  1730. $where[] = sprintf('t1.`timestamp` > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL %s))', $_['date_interval']);
  1731. }
  1732. if ($_['start_date'] !== -1)
  1733. {
  1734. if (!is_int($_['start_date'])) $_['start_date'] = strtotime($_['start_date']);
  1735. $where[] = sprintf('t1.`timestamp` >= %s', $_['start_date']);
  1736. }
  1737. if ($_['end_date'] !== -1)
  1738. {
  1739. if (!is_int($_['end_date'])) $_['end_date'] = strtotime($_['end_date']);
  1740. $where[] = sprintf('t1.`timestamp` <= %s', $_['end_date']);
  1741. }
  1742. if ($_['break_groups'])
  1743. {
  1744. // we select from lifestream_event vs grouped
  1745. $table = 'lifestream_event';
  1746. $cls = 'Lifestream_Event';
  1747. }
  1748. else
  1749. {
  1750. $table = 'lifestream_event_group';
  1751. $cls = 'Lifestream_EventGroup';
  1752. }
  1753. $sql = sprintf("SELECT t1.*, t2.`options` FROM `".$wpdb->prefix.$table."` as `t1` INNER JOIN `".$wpdb->prefix."lifestream_feeds` as t2 ON t1.`feed_id` = t2.`id` WHERE t1.`visible` = 1 AND (%s) ORDER BY t1.`timestamp` DESC LIMIT %d, %d", implode(') AND (', $where), $_['offset'], $_['limit']);
  1754. $results =& $wpdb->get_results($sql);
  1755. $events = array();
  1756. foreach ($results as &$result)
  1757. {
  1758. if (array_key_exists($result->feed, $this->feeds))
  1759. {
  1760. $events[] = new $cls($this, $result);
  1761. }
  1762. }
  1763. return $events;
  1764. }
  1765. }
  1766. $lifestream = new Lifestream();
  1767. function lifestream_get_single_event($feed_type)
  1768. {
  1769. global $lifestream;
  1770. return $lifestream->get_single_event($feed_type);
  1771. }
  1772. require_once(LIFESTREAM_PATH . '/inc/labels.php');
  1773. abstract class Lifestream_Extension
  1774. {
  1775. /**
  1776. * Represents a feed object in the database.
  1777. */
  1778. public $options;
  1779. public static $builtin = false;
  1780. public static $absolute_path = __FILE__;
  1781. // The ID must be a-z, 0-9, _, and - characters. It also must be unique.
  1782. const ID = 'generic';
  1783. const NAME = 'Generic';
  1784. const AUTHOR = 'David Cramer';
  1785. const URL = '';
  1786. const DESCRIPTION = '';
  1787. // Can this feed be grouped?
  1788. const CAN_GROUP = true;
  1789. // Can this feed have a label?
  1790. const MUST_GROUP = false;
  1791. // Labels used in rendering each event
  1792. // params: feed name, event descriptor
  1793. const LABEL = 'Lifestream_Label';
  1794. // The version is so you can manage data in the database for old versions.
  1795. const VERSION = 2;
  1796. const MEDIA = 'automatic';
  1797. const HAS_EXCERPTS = false;
  1798. /**
  1799. * Instantiates this object through a feed database object.
  1800. */
  1801. public static function construct_from_query_result(&$lifestream, $row)
  1802. {
  1803. $class = $lifestream->get_feed($row->feed);
  1804. if (!$class)
  1805. {
  1806. $class = 'Lifestream_InvalidExtension';
  1807. }
  1808. if (!empty($row->options)) $options = unserialize($row->options);
  1809. else $options = null;
  1810. $instance = new $class($lifestream, $options, $row->id, $row);
  1811. $instance->date = $row->timestamp;
  1812. return $instance;
  1813. }
  1814. function __construct(&$lifestream, $options=array(), $id=null, $row=null)
  1815. {
  1816. $this->lifestream = $lifestream;
  1817. $this->options = $options;
  1818. $this->id = $id;
  1819. if ($row)
  1820. {
  1821. $this->active = $row->active;
  1822. $this->owner = $row->owner;
  1823. $this->owner_id = $row->owner_id;
  1824. $this->_owner_id = $row->owner_id;
  1825. $this->version = $row->version;
  1826. $this->events = (int)@($row->events);
  1827. $this->feed = $row->feed;
  1828. }
  1829. else
  1830. {
  1831. $this->version = $this->get_constant('VERSION');
  1832. }
  1833. }
  1834. function __toInt()
  1835. {
  1836. return $this->id;
  1837. }
  1838. function __toString()
  1839. {
  1840. return $this->get_url();
  1841. }
  1842. function get_event_excerpt(&$event, &$bit)
  1843. {
  1844. if (!isset($this->option['excerpts']))
  1845. {
  1846. // default legacy value
  1847. $this->update_option('excerpt', 1);
  1848. }
  1849. if ($this->get_option('excerpt') > 0)
  1850. {
  1851. $excerpt = $this->get_event_description($event, $bit);
  1852. }
  1853. if ($this->get_option('excerpt') == 1)
  1854. {
  1855. $excerpt = $this->lifestream->truncate($excerpt, $this->lifestream->get_option('truncate_length'));
  1856. }
  1857. return $excerpt;
  1858. }
  1859. function has_excerpt(&$event, &$bit)
  1860. {
  1861. if (!isset($this->option['excerpts']))
  1862. {
  1863. // default legacy value
  1864. $this->update_option('excerpt', 1);
  1865. }
  1866. return ($this->get_option('excerpt') > 0 && $this->get_event_description($event, $bit));
  1867. }
  1868. /**
  1869. * Returns the description (also used in excerpts) for this
  1870. * event.
  1871. * @return {String} event description
  1872. */
  1873. function get_event_description(&$event, &$bit)
  1874. {
  1875. return $bit['description'];
  1876. }
  1877. function get_event_display(&$event, &$bit)
  1878. {
  1879. return $bit['title'];
  1880. }
  1881. function get_event_link(&$event, &$bit)
  1882. {
  1883. return $bit['link'];
  1884. }
  1885. function get_feed_display()
  1886. {
  1887. return $this->__toString();
  1888. }
  1889. function get_icon_name()
  1890. {
  1891. return 'icon.png';
  1892. }
  1893. function get_icon_url()
  1894. {
  1895. // TODO: clean this up to use the new Lifestream::get_media methods
  1896. if ($this->get_option('icon_url'))
  1897. {
  1898. return $this->get_option('icon_url');
  1899. }
  1900. $path = trailingslashit($this->lifestream->paths[get_class($this)]);
  1901. $root = trailingslashit(dirname(__FILE__));
  1902. if ($path == $root)
  1903. {
  1904. $path = $this->lifestream->icons[$this->lifestream->get_option('icons', 'default')]['__path'];
  1905. $icon_path = lifestream_path_join($path, $this->get_constant('ID').'.png');
  1906. if (!is_file($icon_path))
  1907. {
  1908. $icon_path = lifestream_path_join($path, 'generic.png');
  1909. }
  1910. $path = $icon_path;
  1911. }
  1912. else
  1913. {
  1914. $path = lifestream_path_join($path, $this->get_icon_name());
  1915. }
  1916. return $this->lifestream->get_absolute_media_url($path);
  1917. }
  1918. function get_public_url()
  1919. {
  1920. return $this->get_constant('URL');
  1921. }
  1922. function get_image_url($row, $item)
  1923. {
  1924. return is_array($item['image']) ? $item['image']['url'] : $item['image'];
  1925. }
  1926. function get_thumbnail_url($row, $item)
  1927. {
  1928. // Array checks are for backwards compatbility
  1929. return is_array($item['thumbnail']) ? $item['thumbnail']['url'] : $item['thumbnail'];
  1930. }
  1931. function get_public_name()
  1932. {
  1933. if ($this->get_option('feed_label'))
  1934. {
  1935. return $this->get_option('feed_label');
  1936. }
  1937. return $this->get_constant('NAME');
  1938. }
  1939. /**
  1940. * Returns a constant attached to this class.
  1941. * @param {string} $constant
  1942. * @return {string | integer} $value
  1943. */
  1944. function get_constant($constant)
  1945. {
  1946. return constant(sprintf('%s::%s', get_class($this), $constant));
  1947. }
  1948. /**
  1949. * Returns an array of available options.
  1950. * @return {array} Available options.
  1951. */
  1952. function get_options()
  1953. {
  1954. return array(
  1955. // key => array(label, required, default value, choices)
  1956. 'url' => array($this->lifestream->__('Feed URL:'), true, '', ''),
  1957. );
  1958. }
  1959. /**
  1960. * Fetches the value of an option. Returns `null` if the option is not set.
  1961. */
  1962. function get_option($option, $default=null)
  1963. {
  1964. if (!isset($this->options[$option]))
  1965. {
  1966. $value = $default;
  1967. }
  1968. else {
  1969. $value = $this->options[$option];
  1970. if (!$value) $value = $default;
  1971. }
  1972. if (empty($value)) $value = null;
  1973. return $value;
  1974. }
  1975. /**
  1976. * Removes an option.
  1977. */
  1978. function delete_option($option)
  1979. {
  1980. unset($this->options[$option]);
  1981. }
  1982. /**
  1983. * Updates the value of an option.
  1984. */
  1985. function update_option($option, $value)
  1986. {
  1987. $this->options[$option] = $value;
  1988. }
  1989. /**
  1990. * Sets an option if it doesn't exist.
  1991. */
  1992. function add_option($option, $value)
  1993. {
  1994. if (!array_key_exists($option, $this->options) || $this->options[$option] === '')
  1995. {
  1996. $this->options[$option] = $value;
  1997. }
  1998. }
  1999. function truncate()
  2000. {
  2001. global $wpdb;
  2002. if ($this->id)
  2003. {
  2004. $wpdb->query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_event` WHERE `feed_id` = %d", $this->id));
  2005. $wpdb->query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_event_group` WHERE `feed_id` = %d", $this->id));
  2006. }
  2007. }
  2008. function save($validate=true)
  2009. {
  2010. global $wpdb;
  2011. $this->save_options($validate);
  2012. // If it has an ID it means it already exists.
  2013. if ($this->id)
  2014. {
  2015. $result = $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_feeds` set `options` = %s, `owner` = %s, `owner_id` = %d WHERE `id` = %d", serialize($this->options), $this->owner, $this->owner_id, $this->id));
  2016. if ($this->_owner_id && $this->_owner_id != $this->owner_id)
  2017. {
  2018. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_event` SET `owner` = %s, `owner_id` = %d WHERE `feed_id` = %d", $this->owner, $this->owner_id, $this->id));
  2019. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_event_group` SET `owner` = %s, `owner_id` = %d WHERE `feed_id` = %d", $this->owner, $this->owner_id, $this->id));
  2020. }
  2021. }
  2022. else
  2023. {
  2024. $result = $wpdb->query($wpdb->prepare("INSERT INTO `".$wpdb->prefix."lifestream_feeds` (`feed`, `options`, `timestamp`, `owner`, `owner_id`, `version`) VALUES (%s, %s, %d, %s, %d, %d)", $this->get_constant('ID'), serialize($this->options), time(), $this->owner, $this->owner_id, $this->get_constant('VERSION')));
  2025. $this->id = $wpdb->insert_id;
  2026. }
  2027. return $result;
  2028. }
  2029. function delete()
  2030. {
  2031. global $wpdb;
  2032. $this->lifestream->safe_query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_feeds` WHERE `id` = %d", $this->id));
  2033. $this->lifestream->safe_query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_event` WHERE `feed_id` = %d", $this->id));
  2034. $this->lifestream->safe_query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_event_group` WHERE `feed_id` = %d", $this->id));
  2035. $this->lifestream->safe_query($wpdb->prepare("DELETE FROM `".$wpdb->prefix."lifestream_error_log` WHERE `feed_id` = %d", $this->id));
  2036. $this->id = null;
  2037. }
  2038. /**
  2039. * Called upon saving options to handle additional data management.
  2040. */
  2041. function save_options($validate=true) { }
  2042. /**
  2043. * Validates the feed. A success has no return value.
  2044. */
  2045. function test()
  2046. {
  2047. try
  2048. {
  2049. $this->save_options($validate=true);
  2050. $this->fetch();
  2051. }
  2052. catch (Lifestream_Error $ex)
  2053. {
  2054. return $ex->getMessage();
  2055. }
  2056. }
  2057. function refresh($urls=null, $initial=false)
  2058. {
  2059. global $wpdb;
  2060. date_default_timezone_set('UTC');
  2061. if (!$this->id) return array(false, $this->lifestream->__('Feed has not yet been saved.'));
  2062. $grouped = array();
  2063. $ungrouped = array();
  2064. $total = 0;
  2065. try
  2066. {
  2067. $items = $this->fetch($urls, $initial);
  2068. }
  2069. catch (Lifestream_Error $ex)
  2070. {
  2071. $this->lifestream->log_error($ex, $this->id);
  2072. return array(false, $ex);
  2073. }
  2074. if (!$items) return array(false, $this->lifestream->__('Feed result was empty.'));
  2075. if (!$initial) $default_timestamp = time();
  2076. else $default_timestamp = 0;
  2077. foreach ($items as $item_key=>&$item)
  2078. {
  2079. // We need to set the default timestamp if no dates are set
  2080. $date = lifestream_array_key_pop($item, 'date');
  2081. $key = lifestream_array_key_pop($item, 'key');
  2082. if (!($date > 0)) $date = $default_timestamp;
  2083. if ($this->version == 2)
  2084. {
  2085. if ($item['guid']) $link_key = md5(lifestream_array_key_pop($item, 'guid'));
  2086. else $link_key = md5($item['link'] . $item['title']);
  2087. }
  2088. elseif ($this->version == 1)
  2089. {
  2090. $link_key = md5($item['link'] . $item['title']);
  2091. }
  2092. else
  2093. {
  2094. $link_key = $item['link'];
  2095. }
  2096. $affected = $wpdb->query($wpdb->prepare("INSERT IGNORE INTO `".$wpdb->prefix."lifestream_event` (`feed_id`, `feed`, `link`, `data`, `timestamp`, `version`, `key`, `owner`, `owner_id`) VALUES (%d, %s, %s, %s, %d, %d, %s, %s, %d)", $this->id, $this->get_constant('ID'), $link_key, serialize($item), $date, $this->get_constant('VERSION'), $key, $this->owner, $this->owner_id));
  2097. if ($affected)
  2098. {
  2099. $item['id'] = $wpdb->insert_id;
  2100. $item['date'] = $date;
  2101. $item['key'] = $key;
  2102. $total += 1;
  2103. $label = $this->get_label_class($key);
  2104. if ($this->get_option('grouped') && $this->get_constant('CAN_GROUP') && constant(sprintf('%s::%s', $label, 'CAN_GROUP')))
  2105. {
  2106. if (!array_key_exists($key, $grouped)) $grouped[$key] = array();
  2107. $grouped[$key][date('m d Y', $date)] = $date;
  2108. }
  2109. else
  2110. {
  2111. $ungrouped[] = $item;
  2112. }
  2113. }
  2114. else
  2115. {
  2116. unset($items[$item_key]);
  2117. }
  2118. }
  2119. // Grouping them by key
  2120. foreach ($grouped as $key=>$dates)
  2121. {
  2122. // Grouping them by date
  2123. foreach ($dates as $date_key=>$date)
  2124. {
  2125. // Get all of the current events for this date
  2126. // (including the one we affected just now)
  2127. $results =& $wpdb->get_results($wpdb->prepare("SELECT `data`, `link` FROM `".$wpdb->prefix."lifestream_event` WHERE `feed_id` = %d AND `visible` = 1 AND DATE(FROM_UNIXTIME(`timestamp`)) = DATE(FROM_UNIXTIME(%d)) AND `key` = %s", $this->id, $date, $key));
  2128. $events = array();
  2129. foreach ($results as &$result)
  2130. {
  2131. $result->data = unserialize($result->data);
  2132. if (!$result->data['link']) $result->data['link'] = $result->link;
  2133. $events[] = $result->data;
  2134. }
  2135. // First let's see if the group already exists in the database
  2136. $group =& $wpdb->get_results($wpdb->prepare("SELECT `id` FROM `".$wpdb->prefix."lifestream_event_group` WHERE `feed_id` = %d AND DATE(FROM_UNIXTIME(`timestamp`)) = DATE(FROM_UNIXTIME(%d)) AND `key` = %s LIMIT 0, 1", $this->id, $date, $key));
  2137. if (count($group) == 1)
  2138. {
  2139. $group =& $group[0];
  2140. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_event_group` SET `data` = %s, `total` = %d, `updated` = 1, `timestamp` = %d WHERE `id` = %d", serialize($events), count($events), $date, $group->id));
  2141. }
  2142. else
  2143. {
  2144. $wpdb->query($wpdb->prepare("INSERT INTO `".$wpdb->prefix."lifestream_event_group` (`feed_id`, `feed`, `data`, `total`, `timestamp`, `version`, `key`, `owner`, `owner_id`) VALUES(%d, %s, %s, %d, %d, %d, %s, %s, %d)", $this->id, $this->get_constant('ID'), serialize($events), count($events), $date, $this->get_constant('VERSION'), $key, $this->owner, $this->owner_id));
  2145. }
  2146. }
  2147. }
  2148. foreach ($ungrouped as &$item)
  2149. {
  2150. $date = lifestream_array_key_pop($item, 'date');
  2151. $key = lifestream_array_key_pop($item, 'key');
  2152. $wpdb->query($wpdb->prepare("INSERT INTO `".$wpdb->prefix."lifestream_event_group` (`feed_id`, `feed`, `event_id`, `data`, `timestamp`, `total`, `version`, `key`, `owner`, `owner_id`) VALUES(%d, %s, %d, %s, %d, 1, %d, %s, %s, %d)", $this->id, $this->get_constant('ID'), $item['id'], serialize(array($item)), $date, $this->get_constant('VERSION'), $key, $this->owner, $this->owner_id));
  2153. }
  2154. $wpdb->query($wpdb->prepare("UPDATE `".$wpdb->prefix."lifestream_feeds` SET `timestamp` = UNIX_TIMESTAMP() WHERE `id` = %d", $this->id));
  2155. unset($items, $ungrouped);
  2156. return array(true, $total);
  2157. }
  2158. /**
  2159. * Processes a row and returns an array of data dictionaries.
  2160. * @return {Array} Array of data dictionaries.
  2161. */
  2162. function yield_many()
  2163. {
  2164. $args = func_get_args();
  2165. $data = call_user_func_array(array(&$this, 'yield'), $args);
  2166. return array($data);
  2167. }
  2168. /**
  2169. * Processes a row and returns a data dictionary.
  2170. * Should at the very least return title and link keys.
  2171. * @abstract
  2172. * @return {Array} Data dictionary.
  2173. */
  2174. //abstract function yield();
  2175. abstract function fetch();
  2176. function get_id($event, $uniq_id='')
  2177. {
  2178. return 'ls-'.$event->id.'-'.$uniq_id;
  2179. }
  2180. function render_item($row, $item)
  2181. {
  2182. $thumbnail = $this->get_thumbnail_url($row, $item);
  2183. if (!empty($thumbnail) && $this->get_constant('MEDIA') == 'automatic')
  2184. {
  2185. $image = $this->get_image_url($row, $item);
  2186. if ($this->lifestream->get_option('use_ibox') == '1' && !empty($image))
  2187. {
  2188. // change it to be large size images
  2189. $ibox = ' rel="ibox&target=\''.htmlspecialchars($image).'\'"';
  2190. }
  2191. else $ibox = '';
  2192. return sprintf('<a href="%s"'.$ibox.' class="photo" title="%s"><img src="%s" width="50" alt="%s"/></a>', htmlspecialchars($item['link']), htmlspecialchars($item['title']), htmlspecialchars($thumbnail), htmlspecialchars($item['title']));
  2193. }
  2194. return sprintf('<a href="%s">%s</a>', htmlspecialchars($item['link']), htmlspecialchars($item['title']));
  2195. }
  2196. function get_label_class($key)
  2197. {
  2198. return $this->get_constant('LABEL');
  2199. }
  2200. function get_label($event, $options=array())
  2201. {
  2202. $cls = $this->get_label_class($event->key);
  2203. return new $cls($this, $event, $options);
  2204. }
  2205. function render($event, $options=array())
  2206. {
  2207. $lifestream = $this->lifestream;
  2208. $label_inst = $event->get_label_instance($options);
  2209. if ($event->is_grouped && count($event->data) == 1 && $this->get_constant('MUST_GROUP')) $visible = true;
  2210. else $visible = isset($options['show_details']) ? !empty($options['show_details']) : null;
  2211. if ($visible === null) $visible = !$this->lifestream->get_option('hide_details_default');
  2212. $filename = $label_inst->get_template();
  2213. require($this->lifestream->get_theme_filepath('templates/'.$filename.'.inc.php'));
  2214. }
  2215. function get_events($limit=50, $offset=0)
  2216. {
  2217. global $wpdb;
  2218. if (!$this->id) return false;
  2219. if (!($limit > 0) || !($offset >= 0)) return false;
  2220. $results =& $wpdb->get_results($wpdb->prepare("SELECT t1.*, t2.`feed`, t2.`options` FROM `".$wpdb->prefix."lifestream_event` as t1 JOIN `".$wpdb->prefix."lifestream_feeds` as t2 ON t1.`feed_id` = t2.`id` WHERE t1.`feed_id` = %d ORDER BY t1.`timestamp` DESC LIMIT %d, %d", $this->id, $offset, $limit));
  2221. $events = array();
  2222. foreach ($results as &$result)
  2223. {
  2224. $events[] = new Lifestream_Event($this->lifestream, $result);
  2225. }
  2226. return $events;
  2227. }
  2228. }
  2229. class Lifestream_InvalidExtension extends Lifestream_Extension
  2230. {
  2231. const NAME = '(The extension could not be found)';
  2232. function get_url()
  2233. {
  2234. return $this->feed;
  2235. }
  2236. function fetch()
  2237. {
  2238. return;
  2239. }
  2240. }
  2241. /**
  2242. * Generic RSS/Atom feed extension.
  2243. */
  2244. class Lifestream_Feed extends Lifestream_Extension
  2245. {
  2246. function save_options($validate=true)
  2247. {
  2248. $urls = $this->get_url();
  2249. if (!is_array($urls)) $urls = array($urls);
  2250. $url = $urls[0];
  2251. if (is_array($url)) $url = $url[0];
  2252. $feed = new SimplePie();
  2253. $feed->enable_cache(false);
  2254. if ($validate)
  2255. {
  2256. $data = $this->lifestream->file_get_contents($url);
  2257. $feed->set_raw_data($data);
  2258. $feed->enable_order_by_date(false);
  2259. $feed->force_feed(true);
  2260. $success = $feed->init();
  2261. }
  2262. if ($this->get_option('auto_icon') && ($url = $feed->get_favicon()))
  2263. {
  2264. if ($this->lifestream->validate_image($url))
  2265. {
  2266. $this->update_option('icon_url', $url);
  2267. }
  2268. else
  2269. {
  2270. $this->update_option('icon_url', '');
  2271. }
  2272. }
  2273. // elseif ($this->get_option('icon_url'))
  2274. // {
  2275. // if (!$this->lifestream->validate_image($this->get_option('icon_url')))
  2276. // {
  2277. // throw new Lifestream_Error($this->lifestream->__('The icon url is not a valid image.'));
  2278. // }
  2279. // }
  2280. parent::save_options();
  2281. }
  2282. /**
  2283. * Fetches all current events from this extension.
  2284. * @return {Array} List of events.
  2285. */
  2286. function fetch($urls=null, $initial=false)
  2287. {
  2288. if (!$urls) $urls = $this->get_url();
  2289. if (!is_array($urls)) $urls = array($urls);
  2290. $items = array();
  2291. foreach ($urls as $url_data)
  2292. {
  2293. if (is_array($url_data))
  2294. {
  2295. // url, key
  2296. list($url, $key) = $url_data;
  2297. }
  2298. else
  2299. {
  2300. $url = $url_data;
  2301. $key = '';
  2302. }
  2303. $feed = new SimplePie();
  2304. $feed->enable_cache(false);
  2305. $data = $this->lifestream->file_get_contents($url);
  2306. $feed->set_raw_data($data);
  2307. $feed->enable_order_by_date(false);
  2308. $feed->force_feed(true);
  2309. $success = $feed->init();
  2310. if (!$success)
  2311. {
  2312. $sample = substr($data, 0, 150);
  2313. throw new Lifestream_FeedFetchError("Error fetching feed from {$url} ({$feed->error()})....\n\n{$sample}");
  2314. }
  2315. $feed->handle_content_type();
  2316. foreach ($feed->get_items() as $row)
  2317. {
  2318. $rows =& $this->yield_many($row, $url, $key);
  2319. foreach ($rows as $row)
  2320. {
  2321. if (!$row) continue;
  2322. if (!$row['key']) $row['key'] = $key;
  2323. if (count($row)) $items[] = $row;
  2324. }
  2325. }
  2326. $feed->__destruct();
  2327. unset($feed);
  2328. }
  2329. return $items;
  2330. }
  2331. function yield($row, $url, $key)
  2332. {
  2333. // date and link are required
  2334. // the rest of the data will be serialized into a `data` field
  2335. // and is pulled out and used on the render($row) method
  2336. $title = $row->get_title();
  2337. if (!$title) return false;
  2338. $data = array(
  2339. 'date' => $row->get_date('U'),
  2340. 'link' => $this->lifestream->html_entity_decode($row->get_link()),
  2341. 'title' => $this->lifestream->html_entity_decode($title),
  2342. 'description' => $this->lifestream->html_entity_decode($row->get_description()),
  2343. 'key' => $key,
  2344. 'guid' => $row->get_id(),
  2345. );
  2346. if ($enclosure = $row->get_enclosure())
  2347. {
  2348. if ($thumbnail = $enclosure->get_thumbnail())
  2349. {
  2350. $data['thumbnail'] = $thumbnail;
  2351. }
  2352. if ($image = $enclosure->get_medium())
  2353. {
  2354. $data['image'] = $image;
  2355. }
  2356. elseif ($image = $enclosure->get_link())
  2357. {
  2358. $data['image'] = $image;
  2359. }
  2360. if (!$data['key']) $data['key'] = 'photo';
  2361. }
  2362. return $data;
  2363. }
  2364. function get_url()
  2365. {
  2366. return $this->get_option('url');
  2367. }
  2368. function parse_urls($text)
  2369. {
  2370. # match http(s):// urls
  2371. $text = preg_replace('@(https?://([-\w\.]+)+(:\d+)?(/([\w\=/\~_\.\%\-]*(\?\S+)?)?)?)@', '<a href="$1">$1</a>', $text);
  2372. # match www urls
  2373. $text = preg_replace('@((?<!http://)www\.([-\w\.]+)+(:\d+)?(/([\w/\=\~_\.\%\-]*(\?\S+)?)?)?)@', '<a href="http://$1">$1</a>', $text);
  2374. # match email@address
  2375. $text = preg_replace('/\b([A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4})\b/i', '<a href="mailto:$1">$1</a>', $text);
  2376. return $text;
  2377. }
  2378. }
  2379. /**
  2380. * You need to pass a thumbnail item in yield() for PhotoFeed item's
  2381. */
  2382. class Lifestream_PhotoFeed extends Lifestream_Feed
  2383. {
  2384. const LABEL = 'Lifestream_PhotoLabel';
  2385. const MUST_GROUP = true;
  2386. }
  2387. class Lifestream_GenericFeed extends Lifestream_Feed {
  2388. const DESCRIPTION = 'The generic feed can handle both feeds with images (in enclosures), as well as your standard text based RSS and Atom feeds.';
  2389. function get_options()
  2390. {
  2391. return array(
  2392. 'url' => array($this->lifestream->__('Feed URL:'), true, '', ''),
  2393. );
  2394. }
  2395. function get_public_url()
  2396. {
  2397. return $this->get_option('url');
  2398. }
  2399. function get_label($event, $options)
  2400. {
  2401. if ($event->key == 'photo') $cls = Lifestream_PhotoFeed::LABEL;
  2402. else $cls = $this->get_constant('LABEL');
  2403. return new $cls($this, $event, $options);
  2404. }
  2405. }
  2406. $lifestream->register_feed('Lifestream_GenericFeed');
  2407. /**
  2408. * Outputs the recent lifestream events.
  2409. * @param {Array} $args An array of keyword args.
  2410. */
  2411. function lifestream($args=array())
  2412. {
  2413. global $lifestream;
  2414. setlocale(LC_ALL, WPLANG);
  2415. $_ = func_get_args();
  2416. $defaults = array(
  2417. 'id' => $lifestream->generate_unique_id(),
  2418. 'limit' => $lifestream->get_option('number_of_items'),
  2419. );
  2420. if (!is_array($_[0]))
  2421. {
  2422. // old style
  2423. $_ = array(
  2424. 'limit' => $_[0],
  2425. 'feed_ids' => $_[1],
  2426. 'date_interval' => $_[2],
  2427. 'user_ids' => $_[4],
  2428. );
  2429. foreach ($_ as $key=>$value)
  2430. {
  2431. if ($value == null) unset($_[$key]);
  2432. }
  2433. }
  2434. else
  2435. {
  2436. $_ = $args;
  2437. }
  2438. $page = $lifestream->get_page_from_request();
  2439. $defaults['offset'] = ($page-1)*(!empty($_['limit']) ? $_['limit'] : $defaults['limit']);
  2440. $_ = array_merge($defaults, $_);
  2441. $limit = $_['limit'];
  2442. $_['limit'] = $_['limit'] + 1;
  2443. $options =& $_;
  2444. // TODO: offset
  2445. //$offset = $lifestream->get_option('lifestream_timezone');
  2446. $events = call_user_func(array(&$lifestream, 'get_events'), $_);
  2447. $has_next_page = (count($events) > $limit);
  2448. if ($has_next_page) {
  2449. $events = array_slice($events, 0, $limit);
  2450. }
  2451. $has_prev_page = ($page > 1);
  2452. $has_paging = ($has_next_page || $has_prev_page);
  2453. $show_metadata = empty($options['hide_metadata']);
  2454. require($lifestream->get_theme_filepath('main.inc.php'));
  2455. echo '<!-- Powered by Lifestream (version: '.LIFESTREAM_VERSION.'; theme: '.$lifestream->get_option('theme', 'default').'; iconset: '.$lifestream->get_option('icons', 'default').') -->';
  2456. if ($lifestream->get_option('show_credits') == '1')
  2457. {
  2458. echo '<p class="lifestream_credits"><small>'.$lifestream->credits().'</small></p>';
  2459. }
  2460. }
  2461. function lifestream_sidebar_widget($_=array())
  2462. {
  2463. global $lifestream;
  2464. setlocale(LC_ALL, WPLANG);
  2465. $defaults = array(
  2466. 'limit' => 10,
  2467. 'break_groups' => true,
  2468. 'show_details' => false,
  2469. );
  2470. $_ = array_merge($defaults, $_);
  2471. $_['id'] = $lifestream->generate_unique_id();
  2472. $options =& $_;
  2473. // TODO: offset
  2474. //$offset = $lifestream->get_option('lifestream_timezone');
  2475. $events = call_user_func(array(&$lifestream, 'get_events'), $_);
  2476. $show_metadata = empty($options['hide_metadata']);
  2477. require($lifestream->get_theme_filepath('sidebar.inc.php'));
  2478. }
  2479. function lifestream_register_feed($class_name)
  2480. {
  2481. global $lifestream;
  2482. $lifestream->register_feed($class_name);
  2483. }
  2484. // built-in feeds
  2485. //include(LIFESTREAM_PATH . '/inc/extensions.php');
  2486. // legacy local_feeds
  2487. // PLEASE READ extensions/README
  2488. @include(LIFESTREAM_PATH . '/local_feeds.inc.php');
  2489. // detect external extensions in extensions/
  2490. $lifestream->detect_extensions();
  2491. $lifestream->detect_themes();
  2492. $lifestream->detect_icons();
  2493. // sort once
  2494. ksort($lifestream->feeds);
  2495. // Require more of the codebase
  2496. require_once(LIFESTREAM_PATH . '/inc/widget.php');
  2497. require_once(LIFESTREAM_PATH . '/inc/syndicate.php');
  2498. ?>