PageRenderTime 97ms CodeModel.GetById 44ms RepoModel.GetById 0ms app.codeStats 1ms

/inc/core.php

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