PageRenderTime 30ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/system/expressionengine/modules/rss/mod.rss.php

https://bitbucket.org/mbaily/tremain
PHP | 519 lines | 458 code | 24 blank | 37 comment | 24 complexity | e0647623f2135de0985c83217183f814 MD5 | raw file
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * ExpressionEngine - by EllisLab
  4. *
  5. * @package ExpressionEngine
  6. * @author EllisLab Dev Team
  7. * @copyright Copyright (c) 2003 - 2013, EllisLab, Inc.
  8. * @license http://ellislab.com/expressionengine/user-guide/license.html
  9. * @link http://ellislab.com
  10. * @since Version 2.0
  11. * @filesource
  12. */
  13. // --------------------------------------------------------------------
  14. /**
  15. * ExpressionEngine RSS Module
  16. *
  17. * @package ExpressionEngine
  18. * @subpackage Modules
  19. * @category Modules
  20. * @author EllisLab Dev Team
  21. * @link http://ellislab.com
  22. */
  23. class Rss {
  24. protected $_debug = FALSE;
  25. /**
  26. * RSS feed
  27. *
  28. * This function fetches the channel metadata used in
  29. * the channel section of RSS feeds
  30. *
  31. * Note: The item elements are generated using the channel class
  32. */
  33. function feed()
  34. {
  35. $this->EE =& get_instance();
  36. ee()->TMPL->encode_email = FALSE;
  37. if (ee()->TMPL->fetch_param('debug') == 'yes')
  38. {
  39. $this->_debug = TRUE;
  40. }
  41. if ( ! $channel = ee()->TMPL->fetch_param('channel'))
  42. {
  43. ee()->lang->loadfile('rss');
  44. return $this->_empty_feed(ee()->lang->line('no_weblog_specified'));
  45. }
  46. ee()->db->select('channel_id');
  47. ee()->functions->ar_andor_string($channel, 'channel_name');
  48. $query = ee()->db->get('channels');
  49. if ($query->num_rows() === 0)
  50. {
  51. ee()->lang->loadfile('rss');
  52. return $this->_empty_feed(ee()->lang->line('rss_invalid_channel'));
  53. }
  54. $tmp = $this->_setup_meta_query($query);
  55. $query = $tmp[0];
  56. $last_update = $tmp[1];
  57. $edit_date = $tmp[2];
  58. $entry_date = $tmp[3];
  59. if ($query->num_rows() === 0)
  60. {
  61. ee()->lang->loadfile('rss');
  62. return $this->_empty_feed(ee()->lang->line('no_matching_entries'));
  63. }
  64. $query = $this->_feed_vars_query($query->row('entry_id'));
  65. $request = ee()->input->request_headers(TRUE);
  66. $start_on = '';
  67. $diffe_request = FALSE;
  68. $feed_request = FALSE;
  69. // Check for 'diff -e' request
  70. if (isset($request['A-IM']) && stristr($request['A-IM'], 'diffe') !== FALSE)
  71. {
  72. $items_start = strpos(ee()->TMPL->tagdata, '{exp:channel:entries');
  73. if ($items_start !== FALSE)
  74. {
  75. // We add three, for three line breaks added later in the script
  76. $diffe_request = count(preg_split("/(\r\n)|(\r)|(\n)/",
  77. trim(substr(ee()->TMPL->tagdata, 0, $items_start)))) + 3;
  78. }
  79. }
  80. // Check for 'feed' request
  81. if (isset($request['A-IM']) && stristr($request['A-IM'],'feed') !== FALSE)
  82. {
  83. $feed_request = TRUE;
  84. $diffe_request = FALSE;
  85. }
  86. // Check for the 'If-Modified-Since' Header
  87. if (ee()->config->item('send_headers') == 'y'
  88. && isset($request['If-Modified-Since'])
  89. && trim($request['If-Modified-Since']) != '')
  90. {
  91. $x = explode(';',$request['If-Modified-Since']);
  92. $modify_tstamp = strtotime($x[0]);
  93. // If new content *and* 'feed' or 'diffe', create start on time.
  94. // Otherwise, we send back a Not Modified header
  95. if ($last_update <= $modify_tstamp)
  96. {
  97. @header("HTTP/1.0 304 Not Modified");
  98. @header('HTTP/1.1 304 Not Modified');
  99. @exit;
  100. }
  101. else
  102. {
  103. if ($diffe_request !== FALSE OR $feed_request !== FALSE)
  104. {
  105. $start_on = ee()->localize->format_date('%Y-%m-%d %h:%i %A', $modify_tstamp);
  106. }
  107. }
  108. }
  109. $chunks = array();
  110. $marker = 'H94e99Perdkie0393e89vqpp';
  111. if (preg_match_all("/{exp:channel:entries.+?{".'\/'."exp:channel:entries}/s",
  112. ee()->TMPL->tagdata, $matches))
  113. {
  114. for($i = 0; $i < count($matches[0]); $i++)
  115. {
  116. ee()->TMPL->tagdata = str_replace($matches[0][$i], $marker.$i, ee()->TMPL->tagdata);
  117. // Remove limit if we have a start_on and dynamic_start
  118. if ($start_on != '' && stristr($matches[0][$i],'dynamic_start="yes"'))
  119. {
  120. $matches[0][$i] = preg_replace("/limit=[\"\'][0-9]{1,5}[\"\']/", '', $matches[0][$i]);
  121. }
  122. // Replace dynamic_start="on" parameter with start_on="" param
  123. $start_on_switch = ($start_on != '') ? 'start_on="'.$start_on.'"' : '';
  124. $matches[0][$i] = preg_replace("/dynamic_start\s*=\s*[\"|']yes[\"|']/i", $start_on_switch, $matches[0][$i]);
  125. $chunks[$marker.$i] = $matches[0][$i];
  126. }
  127. }
  128. // Fetch all the date-related variables
  129. // We do this here to avoid processing cycles in the foreach loop
  130. $entry_date_array = array();
  131. $gmt_date_array = array();
  132. $gmt_entry_date_array = array();
  133. $edit_date_array = array();
  134. $gmt_edit_date_array = array();
  135. $date_vars = array('date', 'gmt_date', 'gmt_entry_date', 'edit_date', 'gmt_edit_date');
  136. foreach ($date_vars as $val)
  137. {
  138. if (preg_match_all("/".LD.$val."\s+format=[\"'](.*?)[\"']".RD."/s", ee()->TMPL->tagdata, $matches))
  139. {
  140. for ($j = 0; $j < count($matches[0]); $j++)
  141. {
  142. $matches[0][$j] = str_replace(LD, '', $matches[0][$j]);
  143. $matches[0][$j] = str_replace(RD, '', $matches[0][$j]);
  144. switch ($val)
  145. {
  146. case 'date' : $entry_date_array[$matches[0][$j]] = $matches[1][$j];
  147. break;
  148. case 'gmt_date' : $gmt_date_array[$matches[0][$j]] = $matches[1][$j];
  149. break;
  150. case 'gmt_entry_date' : $gmt_entry_date_array[$matches[0][$j]] = $matches[1][$j];
  151. break;
  152. case 'edit_date' : $edit_date_array[$matches[0][$j]] = $matches[1][$j];
  153. break;
  154. case 'gmt_edit_date' : $gmt_edit_date_array[$matches[0][$j]] = $matches[1][$j];
  155. break;
  156. }
  157. }
  158. }
  159. }
  160. ee()->load->helper('date');
  161. foreach (ee()->TMPL->var_single as $key => $val)
  162. {
  163. // {date}
  164. if (isset($entry_date_array[$key]))
  165. {
  166. ee()->TMPL->tagdata = ee()->TMPL->swap_var_single(
  167. $key,
  168. ee()->localize->format_date(
  169. $entry_date_array[$key],
  170. $entry_date
  171. ),
  172. ee()->TMPL->tagdata
  173. );
  174. }
  175. // GMT date - entry date in GMT
  176. if (isset($gmt_entry_date_array[$key]))
  177. {
  178. ee()->TMPL->tagdata = ee()->TMPL->swap_var_single(
  179. $key,
  180. ee()->localize->format_date(
  181. $gmt_entry_date_array[$key],
  182. $entry_date,
  183. FALSE
  184. ),
  185. ee()->TMPL->tagdata
  186. );
  187. }
  188. if (isset($gmt_date_array[$key]))
  189. {
  190. ee()->TMPL->tagdata = ee()->TMPL->swap_var_single(
  191. $key,
  192. ee()->localize->format_date(
  193. $gmt_date_array[$key],
  194. $entry_date,
  195. FALSE
  196. ),
  197. ee()->TMPL->tagdata
  198. );
  199. }
  200. // parse "last edit" date
  201. if (isset($edit_date_array[$key]))
  202. {
  203. ee()->TMPL->tagdata = ee()->TMPL->swap_var_single(
  204. $key,
  205. ee()->localize->format_date(
  206. $edit_date_array[$key],
  207. mysql_to_unix($edit_date)
  208. ),
  209. ee()->TMPL->tagdata
  210. );
  211. }
  212. // "last edit" date as GMT
  213. if (isset($gmt_edit_date_array[$key]))
  214. {
  215. ee()->TMPL->tagdata = ee()->TMPL->swap_var_single(
  216. $key,
  217. ee()->localize->format_date(
  218. $gmt_edit_date_array[$key],
  219. mysql_to_unix($edit_date)
  220. ),
  221. ee()->TMPL->tagdata
  222. );
  223. }
  224. }
  225. // Setup {trimmed_url}
  226. $channel_url = $query->row('channel_url');
  227. $trimmed_url = (isset($channel_url) AND $channel_url != '') ? $channel_url : '';
  228. $trimmed_url = str_replace('http://', '', $trimmed_url);
  229. $trimmed_url = str_replace('www.', '', $trimmed_url);
  230. $ex = explode("/", $trimmed_url);
  231. $trimmed_url = current($ex);
  232. $vars = array(
  233. array(
  234. 'channel_id' => $query->row('channel_id'),
  235. 'encoding' => ee()->config->item('output_charset'),
  236. 'channel_language' => $query->row('channel_lang'),
  237. 'channel_description' => $query->row('channel_description'),
  238. 'channel_url' => $query->row('channel_url'),
  239. 'channel_name' => $query->row('channel_title'),
  240. 'email' => $query->row('email'),
  241. 'author' => $query->row('screen_name'),
  242. 'version' => APP_VER,
  243. 'trimmed_url' => $trimmed_url,
  244. ''
  245. )
  246. );
  247. ee()->TMPL->tagdata = ee()->TMPL->parse_variables(ee()->TMPL->tagdata, $vars);
  248. if (count($chunks) > 0)
  249. {
  250. $diff_top = ($start_on != '' && $diffe_request !== false) ? "1,".($diffe_request-1)."c\n" : '';
  251. // Last Update Time
  252. ee()->TMPL->tagdata = '<ee:last_update>'.$last_update."</ee:last_update>\n\n".$diff_top.trim(ee()->TMPL->tagdata);
  253. // Diffe stuff before items
  254. if ($diffe_request !== FALSE)
  255. {
  256. ee()->TMPL->tagdata = str_replace($marker.'0', "\n.\n".$diffe_request."a\n".$marker.'0', ee()->TMPL->tagdata);
  257. ee()->TMPL->tagdata = str_replace($marker.(count($chunks)-1), $marker.(count($chunks)-1)."\n.\n$\n-1\n;c\n", ee()->TMPL->tagdata);
  258. }
  259. foreach ($chunks as $key => $val)
  260. {
  261. ee()->TMPL->tagdata = str_replace($key, $val, ee()->TMPL->tagdata);
  262. }
  263. }
  264. // 'ed' input mode is terminated by entering a single period (.) on a line
  265. ee()->TMPL->tagdata = ($diffe_request !== false) ? trim(ee()->TMPL->tagdata)."\n.\n" : trim(ee()->TMPL->tagdata);
  266. return ee()->TMPL->tagdata;
  267. }
  268. // --------------------------------------------------------------------
  269. /**
  270. * Setup the meta query
  271. *
  272. * @todo -- Convert the query to active record
  273. * @param object query object
  274. * @return object
  275. */
  276. protected function _setup_meta_query($query)
  277. {
  278. // Create Meta Query
  279. //
  280. // Since UTC_TIMESTAMP() is what we need, but it is not available until
  281. // MySQL 4.1.1, we have to use this ever so clever SQL to figure it out:
  282. // DATE_ADD( '1970-01-01', INTERVAL UNIX_TIMESTAMP() SECOND )
  283. $sql = "SELECT exp_channel_titles.entry_id, exp_channel_titles.entry_date, exp_channel_titles.edit_date,
  284. GREATEST((UNIX_TIMESTAMP(exp_channel_titles.edit_date) +
  285. (UNIX_TIMESTAMP(DATE_ADD( '1970-01-01', INTERVAL UNIX_TIMESTAMP() SECOND)) - UNIX_TIMESTAMP())),
  286. exp_channel_titles.entry_date) AS last_update
  287. FROM exp_channel_titles
  288. LEFT JOIN exp_channels ON exp_channel_titles.channel_id = exp_channels.channel_id
  289. LEFT JOIN exp_members ON exp_members.member_id = exp_channel_titles.author_id
  290. WHERE exp_channel_titles.entry_id !=''
  291. AND exp_channels.site_id IN ('".implode("','", ee()->TMPL->site_ids)."') ";
  292. if ($query->num_rows() == 1)
  293. {
  294. $sql .= "AND exp_channel_titles.channel_id = '".$query->row('channel_id') ."' ";
  295. }
  296. else
  297. {
  298. $sql .= "AND (";
  299. foreach ($query->result_array() as $row)
  300. {
  301. $sql .= "exp_channel_titles.channel_id = '".$row['channel_id']."' OR ";
  302. }
  303. $sql = substr($sql, 0, - 3);
  304. $sql .= ") ";
  305. }
  306. // We only select entries that have not expired
  307. $timestamp = (ee()->TMPL->cache_timestamp != '') ? ee()->TMPL->cache_timestamp : ee()->localize->now;
  308. if (ee()->TMPL->fetch_param('show_future_entries') != 'yes')
  309. {
  310. $sql .= " AND exp_channel_titles.entry_date < ".$timestamp." ";
  311. }
  312. if (ee()->TMPL->fetch_param('show_expired') != 'yes')
  313. {
  314. $sql .= " AND (exp_channel_titles.expiration_date = 0 OR exp_channel_titles.expiration_date > ".$timestamp.") ";
  315. }
  316. // Add status declaration
  317. $sql .= "AND exp_channel_titles.status != 'closed' ";
  318. if ($status = ee()->TMPL->fetch_param('status'))
  319. {
  320. $status = str_replace('Open', 'open', $status);
  321. $status = str_replace('Closed', 'closed', $status);
  322. $sql .= ee()->functions->sql_andor_string($status, 'exp_channel_titles.status');
  323. }
  324. else
  325. {
  326. $sql .= "AND exp_channel_titles.status = 'open' ";
  327. }
  328. // Limit to (or exclude) specific users
  329. if ($username = ee()->TMPL->fetch_param('username'))
  330. {
  331. // Shows entries ONLY for currently logged in user
  332. if ($username == 'CURRENT_USER')
  333. {
  334. $sql .= "AND exp_members.member_id = '".ee()->session->userdata['member_id']."' ";
  335. }
  336. elseif ($username == 'NOT_CURRENT_USER')
  337. {
  338. $sql .= "AND exp_members.member_id != '".ee()->session->userdata['member_id']."' ";
  339. }
  340. else
  341. {
  342. $sql .= ee()->functions->sql_andor_string($username, 'exp_members.username');
  343. }
  344. }
  345. // Find Edit Date
  346. $query = ee()->db->query($sql." ORDER BY last_update desc LIMIT 1");
  347. $last_update = '';
  348. $edit_date = '';
  349. $entry_date = '';
  350. if ($query->num_rows() > 0)
  351. {
  352. $last_update = $query->row('last_update');
  353. $edit_date = $query->row('edit_date') ;
  354. $entry_date = $query->row('entry_date') ;
  355. }
  356. $sql .= " ORDER BY exp_channel_titles.entry_date desc LIMIT 1";
  357. $query = ee()->db->query($sql);
  358. return array($query, $last_update, $edit_date, $entry_date);
  359. }
  360. // --------------------------------------------------------------------
  361. /**
  362. * Feed Variables Query
  363. *
  364. * @param integer Entry ID
  365. * @return object db result object
  366. */
  367. protected function _feed_vars_query($entry_id)
  368. {
  369. return ee()->db->select('c.channel_id, c.channel_title, c.channel_url, c.channel_lang,
  370. c.channel_description, ct.entry_date, m.email, m.username,
  371. m.screen_name, m.url')
  372. ->from('channel_titles ct')
  373. ->join('channels c', 'ct.channel_id = c.channel_id', 'left')
  374. ->join('members m', 'm.member_id = ct.author_id', 'left')
  375. ->where('ct.entry_id', (int) $entry_id)
  376. ->where_in('c.site_id', ee()->TMPL->site_ids)
  377. ->get();
  378. }
  379. // --------------------------------------------------------------------
  380. /**
  381. * Empty feed handler
  382. */
  383. private function _empty_feed($error = NULL)
  384. {
  385. if ($error !== NULL)
  386. {
  387. ee()->TMPL->log_item($error);
  388. }
  389. $empty_feed = '';
  390. if (preg_match("/".LD."if empty_feed".RD."(.*?)".LD.'\/'."if".RD."/s", ee()->TMPL->tagdata, $match))
  391. {
  392. if (stristr($match[1], LD.'if'))
  393. {
  394. $match[0] = ee()->functions->full_tag($match[0], ee()->TMPL->tagdata, LD.'if', LD.'\/'."if".RD);
  395. }
  396. $empty_feed = substr($match[0], strlen(LD."if empty_feed".RD), -strlen(LD.'/'."if".RD));
  397. $empty_feed = str_replace(LD.'error'.RD, $error, $empty_feed);
  398. }
  399. if ($empty_feed == '')
  400. {
  401. $empty_feed = $this->_default_empty_feed($error);
  402. }
  403. return $empty_feed;
  404. }
  405. // --------------------------------------------------------------------
  406. /**
  407. * Default empty feed
  408. */
  409. protected function _default_empty_feed($error = '')
  410. {
  411. ee()->lang->loadfile('rss');
  412. $encoding = ee()->config->item('charset');
  413. $title = ee()->config->item('site_name');
  414. $link = ee()->config->item('site_url');
  415. $version = APP_VER;
  416. $pubdate = ee()->localize->format_date('%D, %d %M %Y %H:%i:%s GMT', NULL, FALSE);
  417. $content = ($this->_debug === TRUE && $error != '') ? $error : ee()->lang->line('empty_feed');
  418. return <<<HUMPTYDANCE
  419. <?xml version="1.0" encoding="{$encoding}"?>
  420. <rss version="2.0">
  421. <channel>
  422. <title>{$title}</title>
  423. <link>{$link}</link>
  424. <description></description>
  425. <docs>http://www.rssboard.org/rss-specification</docs>
  426. <generator>ExpressionEngine v{$version} http://ellislab.com/expressionengine</generator>
  427. <item>
  428. <title>{$content}</title>
  429. <description>{$content}</description>
  430. <pubDate>{$pubdate}</pubDate>
  431. </item>
  432. </channel>
  433. </rss>
  434. HUMPTYDANCE;
  435. }
  436. }
  437. // END CLASS
  438. /* End of file mod.rss.php */
  439. /* Location: ./system/expressionengine/modules/rss/mod.rss.php */