PageRenderTime 70ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 2ms

/system/expressionengine/modules/channel/mod.channel.php

https://bitbucket.org/studiobreakfast/sync
PHP | 7660 lines | 5352 code | 1374 blank | 934 comment | 1189 complexity | 4559c0dcc7800d56d939ac7cfa181d00 MD5 | raw file

Large files files are truncated, but you can click here to view the full 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 - 2012, EllisLab, Inc.
  8. * @license http://expressionengine.com/user_guide/license.html
  9. * @link http://expressionengine.com
  10. * @since Version 2.0
  11. * @filesource
  12. */
  13. // --------------------------------------------------------------------
  14. /**
  15. * ExpressionEngine Channel Module
  16. *
  17. * @package ExpressionEngine
  18. * @subpackage Modules
  19. * @category Modules
  20. * @author EllisLab Dev Team
  21. * @link http://expressionengine.com
  22. */
  23. class Channel {
  24. public $limit = '100'; // Default maximum query results if not specified.
  25. // These variable are all set dynamically
  26. public $query;
  27. public $TYPE;
  28. public $entry_id = '';
  29. public $uri = '';
  30. public $uristr = '';
  31. public $return_data = ''; // Final data
  32. public $hit_tracking_id = FALSE;
  33. public $sql = FALSE;
  34. public $cfields = array();
  35. public $dfields = array();
  36. public $rfields = array();
  37. public $mfields = array();
  38. public $pfields = array();
  39. public $categories = array();
  40. public $catfields = array();
  41. public $channel_name = array();
  42. public $channels_array = array();
  43. public $related_entries = array();
  44. public $reverse_related_entries = array();
  45. public $reserved_cat_segment = '';
  46. public $use_category_names = FALSE;
  47. public $cat_request = FALSE;
  48. public $enable = array(); // modified by various tags with disable= parameter
  49. public $absolute_results = NULL; // absolute total results returned by the tag, useful when paginating
  50. public $display_by = '';
  51. // These are used with the nested category trees
  52. public $category_list = array();
  53. public $cat_full_array = array();
  54. public $cat_array = array();
  55. public $temp_array = array();
  56. public $category_count = 0;
  57. public $pagination;
  58. public $pager_sql = '';
  59. // SQL Caching
  60. public $sql_cache_dir = 'sql_cache/';
  61. // Misc. - Class variable usable by extensions
  62. public $misc = FALSE;
  63. /**
  64. * Constructor
  65. */
  66. public function Channel()
  67. {
  68. // Make a local reference to the ExpressionEngine super object
  69. $this->EE =& get_instance();
  70. $this->EE->load->library('pagination');
  71. $this->pagination = new Pagination_object(__CLASS__);
  72. // $this->pagination->per_page = $this->limit;
  73. // Used by pagination to determine whether we're coming from the cache
  74. $this->pagination->dynamic_sql = FALSE;
  75. $this->query_string = ($this->EE->uri->page_query_string != '') ? $this->EE->uri->page_query_string : $this->EE->uri->query_string;
  76. if ($this->EE->config->item("use_category_name") == 'y' && $this->EE->config->item("reserved_category_word") != '')
  77. {
  78. $this->use_category_names = $this->EE->config->item("use_category_name");
  79. $this->reserved_cat_segment = $this->EE->config->item("reserved_category_word");
  80. }
  81. // a number of tags utilize the disable= parameter, set it here
  82. if (isset($this->EE->TMPL) && is_object($this->EE->TMPL))
  83. {
  84. $this->_fetch_disable_param();
  85. }
  86. }
  87. // ------------------------------------------------------------------------
  88. /**
  89. * Initialize values
  90. */
  91. public function initialize()
  92. {
  93. $this->sql = '';
  94. $this->return_data = '';
  95. }
  96. // ------------------------------------------------------------------------
  97. /**
  98. * Fetch Cache
  99. */
  100. public function fetch_cache($identifier = '')
  101. {
  102. $tag = ($identifier == '') ? $this->EE->TMPL->tagproper : $this->EE->TMPL->tagproper.$identifier;
  103. if ($this->EE->TMPL->fetch_param('dynamic_parameters') !== FALSE && isset($_POST) && count($_POST) > 0)
  104. {
  105. foreach (explode('|', $this->EE->TMPL->fetch_param('dynamic_parameters')) as $var)
  106. {
  107. if (isset($_POST[$var]) && in_array($var, array('channel', 'entry_id', 'category', 'orderby', 'sort', 'sticky', 'show_future_entries', 'show_expired', 'entry_id_from', 'entry_id_to', 'not_entry_id', 'start_on', 'stop_before', 'year', 'month', 'day', 'display_by', 'limit', 'username', 'status', 'group_id', 'cat_limit', 'month_limit', 'offset', 'author_id')))
  108. {
  109. $tag .= $var.'="'.$_POST[$var].'"';
  110. }
  111. if (isset($_POST[$var]) && strncmp($var, 'search:', 7) == 0)
  112. {
  113. $tag .= $var.'="'.substr($_POST[$var], 7).'"';
  114. }
  115. }
  116. }
  117. $cache_file = APPPATH.'cache/'.$this->sql_cache_dir.md5($tag.$this->uri);
  118. if ( ! $fp = @fopen($cache_file, FOPEN_READ))
  119. {
  120. return FALSE;
  121. }
  122. flock($fp, LOCK_SH);
  123. $sql = @fread($fp, filesize($cache_file));
  124. flock($fp, LOCK_UN);
  125. fclose($fp);
  126. return $sql;
  127. }
  128. // ------------------------------------------------------------------------
  129. /**
  130. * Save Cache
  131. */
  132. public function save_cache($sql, $identifier = '')
  133. {
  134. $tag = ($identifier == '') ? $this->EE->TMPL->tagproper : $this->EE->TMPL->tagproper.$identifier;
  135. $cache_dir = APPPATH.'cache/'.$this->sql_cache_dir;
  136. $cache_file = $cache_dir.md5($tag.$this->uri);
  137. if ( ! @is_dir($cache_dir))
  138. {
  139. if ( ! @mkdir($cache_dir, DIR_WRITE_MODE))
  140. {
  141. return FALSE;
  142. }
  143. if ($fp = @fopen($cache_dir.'/index.html', FOPEN_WRITE_CREATE_DESTRUCTIVE))
  144. {
  145. fclose($fp);
  146. }
  147. @chmod($cache_dir, DIR_WRITE_MODE);
  148. }
  149. if ( ! $fp = @fopen($cache_file, FOPEN_WRITE_CREATE_DESTRUCTIVE))
  150. {
  151. return FALSE;
  152. }
  153. flock($fp, LOCK_EX);
  154. fwrite($fp, $sql);
  155. flock($fp, LOCK_UN);
  156. fclose($fp);
  157. @chmod($cache_file, FILE_WRITE_MODE);
  158. return TRUE;
  159. }
  160. // ------------------------------------------------------------------------
  161. /**
  162. * Channel entries
  163. */
  164. public function entries()
  165. {
  166. // If the "related_categories" mode is enabled
  167. // we'll call the "related_categories" function
  168. // and bail out.
  169. if ($this->EE->TMPL->fetch_param('related_categories_mode') == 'yes')
  170. {
  171. return $this->related_entries();
  172. }
  173. // Onward...
  174. $this->initialize();
  175. $this->uri = ($this->query_string != '') ? $this->query_string : 'index.php';
  176. if ($this->enable['custom_fields'] == TRUE)
  177. {
  178. $this->fetch_custom_channel_fields();
  179. }
  180. if ($this->enable['member_data'] == TRUE)
  181. {
  182. $this->fetch_custom_member_fields();
  183. }
  184. if ($this->enable['pagination'] == TRUE)
  185. {
  186. $this->pagination->get_template();
  187. }
  188. $save_cache = FALSE;
  189. if ($this->EE->config->item('enable_sql_caching') == 'y' && $this->EE->TMPL->fetch_param('author_id') != 'CURRENT_USER')
  190. {
  191. if (FALSE == ($this->sql = $this->fetch_cache()))
  192. {
  193. $save_cache = TRUE;
  194. }
  195. else
  196. {
  197. if ($this->EE->TMPL->fetch_param('dynamic') != 'no')
  198. {
  199. if (preg_match("#(^|\/)C(\d+)#", $this->query_string, $match) OR in_array($this->reserved_cat_segment, explode("/", $this->query_string)))
  200. {
  201. $this->cat_request = TRUE;
  202. }
  203. }
  204. }
  205. if (FALSE !== ($cache = $this->fetch_cache('pagination_count')))
  206. {
  207. if (FALSE !== ($this->fetch_cache('field_pagination')))
  208. {
  209. if (FALSE !== ($pg_query = $this->fetch_cache('pagination_query')))
  210. {
  211. $this->pagination->paginate = TRUE;
  212. $this->pagination->field_pagination = TRUE;
  213. $this->pagination->cfields = $this->cfields;
  214. $this->pagination->build(trim($cache), $this->sql, $this->EE->db->query(trim($pg_query)));
  215. }
  216. }
  217. else
  218. {
  219. $this->pagination->cfields = $this->cfields;
  220. $this->pagination->build(trim($cache), $this->sql);
  221. }
  222. }
  223. }
  224. if ($this->sql == '')
  225. {
  226. $this->build_sql_query();
  227. }
  228. if ($this->sql == '')
  229. {
  230. return $this->EE->TMPL->no_results();
  231. }
  232. if ($save_cache == TRUE)
  233. {
  234. $this->save_cache($this->sql);
  235. }
  236. $this->query = $this->EE->db->query($this->sql);
  237. if ($this->query->num_rows() == 0)
  238. {
  239. return $this->EE->TMPL->no_results();
  240. }
  241. // -------------------------------------
  242. // "Relaxed" View Tracking
  243. //
  244. // Some people have tags that are used to mimic a single-entry
  245. // page without it being dynamic. This allows Entry View Tracking
  246. // to work for ANY combination that results in only one entry
  247. // being returned by the tag, including channel query caching.
  248. //
  249. // Hidden Configuration Variable
  250. // - relaxed_track_views => Allow view tracking on non-dynamic
  251. // single entries (y/n)
  252. // -------------------------------------
  253. if ($this->EE->config->item('relaxed_track_views') === 'y' && $this->query->num_rows() == 1)
  254. {
  255. $this->hit_tracking_id = $this->query->row('entry_id') ;
  256. }
  257. $this->track_views();
  258. $this->EE->load->library('typography');
  259. $this->EE->typography->initialize(array(
  260. 'convert_curly' => FALSE
  261. ));
  262. if ($this->enable['categories'] == TRUE)
  263. {
  264. $this->fetch_categories();
  265. }
  266. $this->parse_channel_entries();
  267. if ($this->enable['pagination'] == TRUE)
  268. {
  269. $this->return_data = $this->pagination->render($this->return_data);
  270. }
  271. // Does the tag contain "related entries" that we need to parse out?
  272. if (count($this->EE->TMPL->related_data) > 0 && count($this->related_entries) > 0)
  273. {
  274. $this->parse_related_entries();
  275. }
  276. if (count($this->EE->TMPL->reverse_related_data) > 0 && count($this->reverse_related_entries) > 0)
  277. {
  278. $this->parse_reverse_related_entries();
  279. }
  280. return $this->return_data;
  281. }
  282. // ------------------------------------------------------------------------
  283. /**
  284. * Process related entries
  285. */
  286. public function parse_related_entries()
  287. {
  288. $sql = "SELECT rel_id, rel_parent_id, rel_child_id, rel_type, rel_data
  289. FROM exp_relationships
  290. WHERE rel_id IN (";
  291. $templates = array();
  292. foreach ($this->related_entries as $val)
  293. {
  294. $x = explode('_', $val);
  295. $sql .= "'".$x[0]."',";
  296. $templates[] = array($x[0], $x[1], $this->EE->TMPL->related_data[$x[1]]);
  297. }
  298. $sql = substr($sql, 0, -1).')';
  299. $query = $this->EE->db->query($sql);
  300. if ($query->num_rows() == 0)
  301. return;
  302. // --------------------------------
  303. // Without this the Related Entries were inheriting the parameters of
  304. // the enclosing Channel Entries tag. Sometime in the future we will
  305. // likely allow Related Entries to have their own parameters
  306. // --------------------------------
  307. $return_data = $this->return_data;
  308. foreach ($templates as $temp)
  309. {
  310. foreach ($query->result_array() as $row)
  311. {
  312. if ($row['rel_id'] != $temp[0])
  313. continue;
  314. // --------------------------------------
  315. // If the data is emptied (cache cleared), then we
  316. // rebuild it with fresh data so processing can continue.
  317. // --------------------------------------
  318. if (trim($row['rel_data']) == '')
  319. {
  320. $rewrite = array(
  321. 'type' => $row['rel_type'],
  322. 'parent_id' => $row['rel_parent_id'],
  323. 'child_id' => $row['rel_child_id'],
  324. 'related_id' => $row['rel_id']
  325. );
  326. $this->EE->functions->compile_relationship($rewrite, FALSE);
  327. $results = $this->EE->db->query("SELECT rel_data FROM exp_relationships WHERE rel_id = '".$row['rel_id']."'");
  328. $row['rel_data'] = $results->row('rel_data') ;
  329. }
  330. // Begin Processing
  331. $this->initialize();
  332. if ($reldata = @unserialize($row['rel_data']))
  333. {
  334. $this->EE->TMPL->var_single = $temp[2]['var_single'];
  335. $this->EE->TMPL->var_pair = $temp[2]['var_pair'];
  336. $this->EE->TMPL->var_cond = $temp[2]['var_cond'];
  337. $this->EE->TMPL->tagdata = $temp[2]['tagdata'];
  338. if ($row['rel_type'] == 'channel')
  339. {
  340. // Bug fix for when categories were not being inserted
  341. // correctly for related channel entries. Bummer.
  342. if (count($reldata['categories'] == 0) && ! isset($reldata['cats_fixed']))
  343. {
  344. $fixdata = array(
  345. 'type' => $row['rel_type'],
  346. 'parent_id' => $row['rel_parent_id'],
  347. 'child_id' => $row['rel_child_id'],
  348. 'related_id' => $row['rel_id']
  349. );
  350. $this->EE->functions->compile_relationship($fixdata, FALSE);
  351. $reldata['categories'] = $this->EE->functions->cat_array;
  352. $reldata['category_fields'] = $this->EE->functions->catfields;
  353. }
  354. $this->query = $reldata['query'];
  355. if ($this->query->num_rows != 0)
  356. {
  357. $this->categories = array($this->query->row('entry_id') => $reldata['categories']);
  358. if (isset($reldata['category_fields']))
  359. {
  360. $this->catfields = array($this->query->row('entry_id') => $reldata['category_fields']);
  361. }
  362. }
  363. $this->parse_channel_entries();
  364. $marker = LD."REL[".$row['rel_id']."][".$temp[2]['field_name']."]".$temp[1]."REL".RD;
  365. $return_data = str_replace($marker, $this->return_data, $return_data);
  366. }
  367. }
  368. }
  369. }
  370. $this->return_data = $return_data;
  371. }
  372. // ------------------------------------------------------------------------
  373. /**
  374. * Process reverse related entries
  375. */
  376. public function parse_reverse_related_entries()
  377. {
  378. $this->EE->db->select('rel_id, rel_parent_id, rel_child_id, rel_type, reverse_rel_data');
  379. $this->EE->db->where_in('rel_child_id', array_keys($this->reverse_related_entries));
  380. $this->EE->db->where('rel_type', 'channel');
  381. $query = $this->EE->db->get('relationships');
  382. if ($query->num_rows() == 0)
  383. {
  384. // remove Reverse Related tags for these entries
  385. foreach ($this->reverse_related_entries as $entry_id => $templates)
  386. {
  387. foreach($templates as $tkey => $template)
  388. {
  389. $this->return_data = str_replace(LD."REV_REL[".$this->EE->TMPL->reverse_related_data[$template]['marker']."][".$entry_id."]REV_REL".RD, $this->EE->TMPL->reverse_related_data[$template]['no_rev_content'], $this->return_data);
  390. }
  391. }
  392. return;
  393. }
  394. // Data Processing Time
  395. $entry_data = array();
  396. for ($i = 0, $total = count($query->result_array()); $i < $total; $i++)
  397. {
  398. $row = array_shift($query->result_array);
  399. // If the data is emptied (cache cleared or first process), then we
  400. // rebuild it with fresh data so processing can continue.
  401. if (trim($row['reverse_rel_data']) == '')
  402. {
  403. $rewrite = array(
  404. 'type' => $row['rel_type'],
  405. 'parent_id' => $row['rel_parent_id'],
  406. 'child_id' => $row['rel_child_id'],
  407. 'related_id' => $row['rel_id']
  408. );
  409. $this->EE->functions->compile_relationship($rewrite, FALSE, TRUE);
  410. $this->EE->db->select('reverse_rel_data');
  411. $this->EE->db->where('rel_parent_id', $row['rel_parent_id']);
  412. $results = $this->EE->db->get('relationships');
  413. $row['reverse_rel_data'] = $results->row('reverse_rel_data');
  414. }
  415. // Unserialize the entries data, please
  416. if ($revreldata = @unserialize($row['reverse_rel_data']))
  417. {
  418. $entry_data[$row['rel_child_id']][$row['rel_parent_id']] = $revreldata;
  419. }
  420. }
  421. // Without this the Reverse Related Entries were inheriting the parameters of
  422. // the enclosing Channel Entries tag, which is not appropriate.
  423. $return_data = $this->return_data;
  424. foreach ($this->reverse_related_entries as $entry_id => $templates)
  425. {
  426. // No Entries? Remove Reverse Related Tags and Continue to Next Entry
  427. if ( ! isset($entry_data[$entry_id]))
  428. {
  429. foreach($templates as $tkey => $template)
  430. {
  431. $return_data = str_replace(LD."REV_REL[".$this->EE->TMPL->reverse_related_data[$template]['marker']."][".$entry_id."]REV_REL".RD, $this->EE->TMPL->reverse_related_data[$template]['no_rev_content'], $return_data);
  432. }
  433. continue;
  434. }
  435. // Process Our Reverse Related Templates
  436. foreach($templates as $tkey => $template)
  437. {
  438. $i = 0;
  439. $cats = array();
  440. $params = $this->EE->TMPL->reverse_related_data[$template]['params'];
  441. if ( ! is_array($params))
  442. {
  443. $params = array('status' => 'open');
  444. }
  445. elseif ( ! isset($params['status']))
  446. {
  447. $params['status'] = 'open';
  448. }
  449. else
  450. {
  451. $params['status'] = trim($params['status'], " |\t\n\r");
  452. }
  453. // Entries have to be ordered, sorted and other stuff
  454. $new = array();
  455. $order = ( ! isset($params['orderby'])) ? 'date' : $params['orderby'];
  456. $offset = ( ! isset($params['offset']) OR ! is_numeric($params['offset'])) ? 0 : $params['offset'];
  457. $limit = ( ! isset($params['limit']) OR ! is_numeric($params['limit'])) ? 100 : $params['limit'];
  458. $sort = ( ! isset($params['sort'])) ? 'asc' : $params['sort'];
  459. $random = ($order == 'random') ? TRUE : FALSE;
  460. $base_orders = array('random', 'date', 'title', 'url_title', 'edit_date', 'comment_total', 'username', 'screen_name', 'most_recent_comment', 'expiration_date', 'entry_id',
  461. 'view_count_one', 'view_count_two', 'view_count_three', 'view_count_four');
  462. $str_sort = array('title', 'url_title', 'username', 'screen_name');
  463. if ( ! in_array($order, $base_orders))
  464. {
  465. $set = 'n';
  466. foreach($this->cfields as $site_id => $cfields)
  467. {
  468. if ( isset($cfields[$order]))
  469. {
  470. $multi_order[] = 'field_id_'.$cfields[$order];
  471. $set = 'y';
  472. $str_sort[] = 'field_id_'.$cfields[$order];
  473. //break;
  474. }
  475. }
  476. if ( $set == 'n' )
  477. {
  478. $order = 'date';
  479. }
  480. }
  481. if ($order == 'date' OR $order == 'random')
  482. {
  483. $order = 'entry_date';
  484. }
  485. if (isset($params['channel']) && trim($params['channel']) != '')
  486. {
  487. if (count($this->channels_array) == 0)
  488. {
  489. $this->EE->db->select('channel_id, channel_name');
  490. $this->EE->db->where_in('site_id', $this->EE->TMPL->site_ids);
  491. $results = $this->EE->db->get('channels');
  492. foreach($results->result_array() as $row)
  493. {
  494. $this->channels_array[$row['channel_id']] = $row['channel_name'];
  495. }
  496. }
  497. $channels = explode('|', trim($params['channel']));
  498. $allowed = array();
  499. if (strncmp($channels[0], 'not ', 4) == 0)
  500. {
  501. $channels[0] = trim(substr($channels[0], 3));
  502. $allowed = $this->channels_array;
  503. foreach($channels as $name)
  504. {
  505. if (in_array($name, $allowed))
  506. {
  507. foreach (array_keys($allowed, $name) AS $k)
  508. {
  509. unset($allowed[$k]);
  510. }
  511. }
  512. }
  513. }
  514. else
  515. {
  516. foreach($channels as $name)
  517. {
  518. if (in_array($name, $this->channels_array))
  519. {
  520. foreach (array_keys($this->channels_array, $name) AS $k)
  521. {
  522. $allowed[$k] = $name;
  523. }
  524. }
  525. }
  526. }
  527. }
  528. $stati = explode('|', $params['status']);
  529. $stati = array_map('strtolower', $stati); // match MySQL's case-insensitivity
  530. $status_state = 'positive';
  531. // Check for "not "
  532. if (substr($stati[0], 0, 4) == 'not ')
  533. {
  534. $status_state = 'negative';
  535. $stati[0] = trim(substr($stati[0], 3));
  536. $stati[] = 'closed';
  537. }
  538. $r = 1; // Fixes a problem when a sorting key occurs twice
  539. foreach($entry_data[$entry_id] as $relating_data)
  540. {
  541. $post_fix = ' '.$r;
  542. $order_set = FALSE;
  543. if ( ! isset($params['channel']) OR ($relating_data['query']->row('channel_id') && array_key_exists($relating_data['query']->row('channel_id'), $allowed)))
  544. {
  545. $query_row = $relating_data['query']->row_array();
  546. if (isset($multi_order))
  547. {
  548. foreach ($multi_order as $field_val)
  549. {
  550. if (isset($query_row[$field_val]))
  551. {
  552. $order_set = TRUE;
  553. $order_key = '';
  554. if ($query_row[$field_val] != '')
  555. {
  556. $order_key = $query_row[$field_val];
  557. $order = $field_val;
  558. break;
  559. }
  560. }
  561. }
  562. }
  563. elseif (isset($query_row[$order]))
  564. {
  565. $order_set = TRUE;
  566. $order_key = $query_row[$order];
  567. }
  568. // Needs to have the field we're ordering by
  569. if ($order_set)
  570. {
  571. if ($status_state == 'negative' && ! in_array(strtolower($query_row['status']) , $stati))
  572. {
  573. $new[$order_key.$post_fix] = $relating_data;
  574. }
  575. elseif (in_array(strtolower($query_row['status']) , $stati))
  576. {
  577. $new[$order_key.$post_fix] = $relating_data;
  578. }
  579. }
  580. ++$r;
  581. }
  582. }
  583. $sort_flags = SORT_REGULAR;
  584. // Check if the custom field to sort on is numeric, sort numericaly if it is
  585. if (strncmp($order, 'field_id_', 9) === 0)
  586. {
  587. $this->EE->load->library('api');
  588. $this->EE->api->instantiate('channel_fields');
  589. $field_settings = $this->EE->api_channel_fields->get_settings(substr($order, 9));
  590. if (isset($field_settings['field_content_type']) && in_array($field_settings['field_content_type'], array('numeric', 'integer', 'decimal')))
  591. {
  592. $sort_flags = SORT_NUMERIC;
  593. }
  594. }
  595. // Shuffle if random
  596. if ($random === TRUE)
  597. {
  598. shuffle($new);
  599. }
  600. else
  601. {
  602. // Sort keys by string comparison
  603. if (in_array($order, $str_sort))
  604. {
  605. uksort($new, 'strnatcasecmp');
  606. }
  607. // If it's in the base options and not a string?
  608. // Sort numeric
  609. elseif (in_array($order, $base_orders))
  610. {
  611. ksort($new, SORT_NUMERIC);
  612. }
  613. // Sort keys based on set sort flags
  614. else
  615. {
  616. ksort($new, $sort_flags);
  617. }
  618. // Reverse sorted array if we're sorting descending
  619. if ($sort != 'asc')
  620. {
  621. $new = array_reverse($new, TRUE);
  622. }
  623. }
  624. $output_data[$entry_id] = array_slice($new, $offset, $limit);
  625. if (count($output_data[$entry_id]) == 0)
  626. {
  627. $return_data = str_replace(LD."REV_REL[".$this->EE->TMPL->reverse_related_data[$template]['marker']."][".$entry_id."]REV_REL".RD, $this->EE->TMPL->reverse_related_data[$template]['no_rev_content'], $return_data);
  628. continue;
  629. }
  630. // Finally! We get to process our parents
  631. foreach($output_data[$entry_id] as $relating_data)
  632. {
  633. if ($i == 0)
  634. {
  635. $query = clone $relating_data['query'];
  636. }
  637. else
  638. {
  639. $query->result_array[] = $relating_data['query']->row_array();
  640. }
  641. $cats[$relating_data['query']->row('entry_id') ] = $relating_data['categories'];
  642. ++$i;
  643. }
  644. $query->num_rows = $i;
  645. $this->initialize();
  646. $this->EE->TMPL->var_single = $this->EE->TMPL->reverse_related_data[$template]['var_single'];
  647. $this->EE->TMPL->var_pair = $this->EE->TMPL->reverse_related_data[$template]['var_pair'];
  648. $this->EE->TMPL->var_cond = $this->EE->TMPL->reverse_related_data[$template]['var_cond'];
  649. $this->EE->TMPL->tagdata = $this->EE->TMPL->reverse_related_data[$template]['tagdata'];
  650. $this->query = $query;
  651. $this->categories = $cats;
  652. $this->parse_channel_entries();
  653. $return_data = str_replace( LD."REV_REL[".$this->EE->TMPL->reverse_related_data[$template]['marker']."][".$entry_id."]REV_REL".RD,
  654. $this->return_data,
  655. $return_data);
  656. }
  657. }
  658. $this->return_data = $return_data;
  659. }
  660. // ------------------------------------------------------------------------
  661. /**
  662. * Track Views
  663. */
  664. public function track_views()
  665. {
  666. if ($this->EE->config->item('enable_entry_view_tracking') == 'n')
  667. {
  668. return;
  669. }
  670. if ( ! $this->EE->TMPL->fetch_param('track_views') OR $this->hit_tracking_id === FALSE)
  671. {
  672. return;
  673. }
  674. if ($this->pagination->field_pagination == TRUE AND $this->pagination->offset > 0)
  675. {
  676. return;
  677. }
  678. foreach (explode('|', $this->EE->TMPL->fetch_param('track_views')) as $view)
  679. {
  680. if ( ! in_array(strtolower($view), array("one", "two", "three", "four")))
  681. {
  682. continue;
  683. }
  684. $sql = "UPDATE exp_channel_titles SET view_count_{$view} = (view_count_{$view} + 1) WHERE ";
  685. $sql .= (is_numeric($this->hit_tracking_id)) ? "entry_id = {$this->hit_tracking_id}" : "url_title = '".$this->EE->db->escape_str($this->hit_tracking_id)."'";
  686. $this->EE->db->query($sql);
  687. }
  688. }
  689. // ------------------------------------------------------------------------
  690. /**
  691. * Fetch custom channel field IDs
  692. */
  693. public function fetch_custom_channel_fields()
  694. {
  695. if (isset($this->EE->session->cache['channel']['custom_channel_fields']) && isset($this->EE->session->cache['channel']['date_fields'])
  696. && isset($this->EE->session->cache['channel']['relationship_fields']) && isset($this->EE->session->cache['channel']['pair_custom_fields']))
  697. {
  698. $this->cfields = $this->EE->session->cache['channel']['custom_channel_fields'];
  699. $this->dfields = $this->EE->session->cache['channel']['date_fields'];
  700. $this->rfields = $this->EE->session->cache['channel']['relationship_fields'];
  701. $this->pfields = $this->EE->session->cache['channel']['pair_custom_fields'];
  702. return;
  703. }
  704. $this->EE->load->library('api');
  705. $this->EE->api->instantiate('channel_fields');
  706. $fields = $this->EE->api_channel_fields->fetch_custom_channel_fields();
  707. $this->cfields = $fields['custom_channel_fields'];
  708. $this->dfields = $fields['date_fields'];
  709. $this->rfields = $fields['relationship_fields'];
  710. $this->pfields = $fields['pair_custom_fields'];
  711. $this->EE->session->cache['channel']['custom_channel_fields'] = $this->cfields;
  712. $this->EE->session->cache['channel']['date_fields'] = $this->dfields;
  713. $this->EE->session->cache['channel']['relationship_fields'] = $this->rfields;
  714. $this->EE->session->cache['channel']['pair_custom_fields'] = $this->pfields;
  715. }
  716. // ------------------------------------------------------------------------
  717. /**
  718. * Fetch custom member field IDs
  719. */
  720. public function fetch_custom_member_fields()
  721. {
  722. $this->EE->db->select('m_field_id, m_field_name, m_field_fmt');
  723. $query = $this->EE->db->get('member_fields');
  724. $fields_present = FALSE;
  725. $t1 = microtime(TRUE);
  726. foreach ($query->result_array() as $row)
  727. {
  728. if (strpos($this->EE->TMPL->tagdata, $row['m_field_name']) !== FALSE)
  729. {
  730. $fields_present = TRUE;
  731. }
  732. $this->mfields[$row['m_field_name']] = array($row['m_field_id'], $row['m_field_fmt']);
  733. }
  734. // If we can find no instance of the variable, then let's not process them at all.
  735. if ($fields_present === FALSE)
  736. {
  737. $this->mfields = array();
  738. }
  739. }
  740. // ------------------------------------------------------------------------
  741. /**
  742. * Fetch categories
  743. */
  744. public function fetch_categories()
  745. {
  746. if ($this->enable['category_fields'] === TRUE)
  747. {
  748. $query = $this->EE->db->query("SELECT field_id, field_name FROM exp_category_fields WHERE site_id IN ('".implode("','", $this->EE->TMPL->site_ids)."')");
  749. if ($query->num_rows() > 0)
  750. {
  751. foreach ($query->result_array() as $row)
  752. {
  753. $this->catfields[] = array('field_name' => $row['field_name'], 'field_id' => $row['field_id']);
  754. }
  755. }
  756. $field_sqla = ", cg.field_html_formatting, fd.* ";
  757. $field_sqlb = " LEFT JOIN exp_category_field_data AS fd ON fd.cat_id = c.cat_id
  758. LEFT JOIN exp_category_groups AS cg ON cg.group_id = c.group_id";
  759. }
  760. else
  761. {
  762. $field_sqla = '';
  763. $field_sqlb = '';
  764. }
  765. $sql = "SELECT c.cat_name, c.cat_url_title, c.cat_id, c.cat_image, c.cat_description, c.parent_id,
  766. p.cat_id, p.entry_id, c.group_id {$field_sqla}
  767. FROM (exp_categories AS c, exp_category_posts AS p)
  768. {$field_sqlb}
  769. WHERE c.cat_id = p.cat_id
  770. AND p.entry_id IN (";
  771. $categories = array();
  772. foreach ($this->query->result_array() as $row)
  773. {
  774. $sql .= "'".$row['entry_id']."',";
  775. $categories[] = $row['entry_id'];
  776. }
  777. $sql = substr($sql, 0, -1).')';
  778. $sql .= " ORDER BY c.group_id, c.parent_id, c.cat_order";
  779. $query = $this->EE->db->query($sql);
  780. if ($query->num_rows() == 0)
  781. {
  782. return;
  783. }
  784. foreach ($categories as $val)
  785. {
  786. $this->temp_array = array();
  787. $this->cat_array = array();
  788. $parents = array();
  789. foreach ($query->result_array() as $row)
  790. {
  791. if ($val == $row['entry_id'])
  792. {
  793. $this->temp_array[$row['cat_id']] = array($row['cat_id'], $row['parent_id'], $row['cat_name'], $row['cat_image'], $row['cat_description'], $row['group_id'], $row['cat_url_title']);
  794. foreach ($row as $k => $v)
  795. {
  796. if (strpos($k, 'field') !== FALSE)
  797. {
  798. $this->temp_array[$row['cat_id']][$k] = $v;
  799. }
  800. }
  801. if ($row['parent_id'] > 0 && ! isset($this->temp_array[$row['parent_id']])) $parents[$row['parent_id']] = '';
  802. unset($parents[$row['cat_id']]);
  803. }
  804. }
  805. if (count($this->temp_array) == 0)
  806. {
  807. $temp = FALSE;
  808. }
  809. else
  810. {
  811. foreach($this->temp_array as $k => $v)
  812. {
  813. if (isset($parents[$v[1]])) $v[1] = 0;
  814. if (0 == $v[1])
  815. {
  816. $this->cat_array[] = $this->temp_array[$k];
  817. $this->process_subcategories($k);
  818. }
  819. }
  820. }
  821. $this->categories[$val] = $this->cat_array;
  822. }
  823. unset($this->temp_array);
  824. unset($this->cat_array);
  825. }
  826. // ------------------------------------------------------------------------
  827. /****************************************************************
  828. * Field Searching
  829. *
  830. * Generate the sql for the where clause to implement field
  831. * searching. Implements cross site field searching with a
  832. * sloppy search, IE if there are any fields with the same name
  833. * in any of the sites specified in the [ site="" ] parameter then
  834. * all of those fields will be searched.
  835. *
  836. *****************************************************************/
  837. protected function generate_field_search_sql($search_fields, $site_ids)
  838. {
  839. $sql = '';
  840. foreach ($search_fields as $field_name => $search_terms)
  841. {
  842. $fields_sql = '';
  843. $sites = ($site_ids ? $site_ids : array($this->EE->config->item('site_id')));
  844. foreach ($sites as $site_name => $site_id)
  845. {
  846. $terms = $search_terms;
  847. if ( ! isset($this->cfields[$site_id][$field_name]))
  848. {
  849. continue;
  850. }
  851. if (strncmp($terms, '=', 1) == 0)
  852. {
  853. /** ---------------------------------------
  854. /** Exact Match e.g.: search:body="=pickle"
  855. /** ---------------------------------------*/
  856. $terms = substr($terms, 1);
  857. // special handling for IS_EMPTY
  858. if (strpos($terms, 'IS_EMPTY') !== FALSE)
  859. {
  860. // Did this because I don't like repeatedly checking
  861. // the beginning of the string with strncmp for that
  862. // 'not', much prefer to do it once and then set a
  863. // boolean. But..
  864. $not = false;
  865. if (strncmp($terms, 'not ', 4) == 0)
  866. {
  867. $not = true;
  868. $terms = substr($terms, 4);
  869. }
  870. if (strpos($terms, '|') !== false)
  871. {
  872. $terms = str_replace('IS_EMPTY|', '', $terms);
  873. }
  874. else
  875. {
  876. $terms = str_replace('IS_EMPTY', '', $terms);
  877. }
  878. $add_search = '';
  879. $conj = '';
  880. if ( ! empty($terms))
  881. {
  882. // ...it makes this a little hacky. Gonna leave it for the moment,
  883. // but may come back to it.
  884. $add_search = $this->EE->functions->sql_andor_string(($not ? 'not ' . $terms : $terms), 'wd.field_id_'.$this->cfields[$site_id][$field_name]);
  885. // remove the first AND output by $this->EE->functions->sql_andor_string() so we can parenthesize this clause
  886. $add_search = '(wd.site_id=' . $site_id . ' AND ' . substr($add_search, 3) . ')';
  887. $conj = ($add_search != '' && !$not) ? 'OR' : 'AND';
  888. }
  889. if ($not)
  890. {
  891. $fields_sql .= $add_search.' '.$conj.' (wd.site_id=' . $site_id . ' AND wd.field_id_'.$this->cfields[$site_id][$field_name].' != "")';
  892. }
  893. else
  894. {
  895. $fields_sql .= $add_search.' '.$conj.' (wd.site_id=' . $site_id . ' AND wd.field_id_'.$this->cfields[$site_id][$field_name].' = "")';
  896. }
  897. }
  898. else
  899. {
  900. $fields_sql .= substr($this->EE->functions->sql_andor_string($terms, 'wd.field_id_'.$this->cfields[$site_id][$field_name]), 3).' ';
  901. }
  902. }
  903. else
  904. {
  905. /** ---------------------------------------
  906. /** "Contains" e.g.: search:body="pickle"
  907. /** ---------------------------------------*/
  908. $not = '';
  909. if (strncmp($terms, 'not ', 4) == 0)
  910. {
  911. $terms = substr($terms, 4);
  912. $not = 'NOT';
  913. }
  914. if (strpos($terms, '&&') !== FALSE)
  915. {
  916. $terms = explode('&&', $terms);
  917. $andor = $not == 'NOT' ? 'OR' : 'AND';
  918. }
  919. else
  920. {
  921. $terms = explode('|', $terms);
  922. $andor = $not == 'NOT' ? 'AND' : 'OR';
  923. }
  924. foreach ($terms as $term)
  925. {
  926. if ($term == 'IS_EMPTY')
  927. {
  928. $fields_sql .= ' (wd.site_id=' . $site_id
  929. . ' AND wd.field_id_' . $this->cfields[$site_id][$field_name] . ($not=='NOT' ? '!' : '') . '="") '
  930. . $andor;
  931. }
  932. elseif (strpos($term, '\W') !== FALSE) // full word only, no partial matches
  933. {
  934. // Note: MySQL's nutty POSIX regex word boundary is [[:>:]]
  935. $term = '([[:<:]]|^)'.preg_quote(str_replace('\W', '', $term)).'([[:>:]]|$)';
  936. $fields_sql .= ' (wd.site_id=' . $site_id
  937. . ' AND wd.field_id_' . $this->cfields[$site_id][$field_name] . ' ' . $not
  938. . ' REGEXP "' . $this->EE->db->escape_str($term).'") '
  939. . $andor;
  940. }
  941. else
  942. {
  943. $fields_sql .= ' (wd.site_id=' . $site_id
  944. . ' AND wd.field_id_' . $this->cfields[$site_id][$field_name]
  945. . $not . ' LIKE "%' . $this->EE->db->escape_like_str($term) . '%") '
  946. . $andor;
  947. }
  948. }
  949. // Remove the extra "and" or "or".
  950. $fields_sql = substr($fields_sql, 0, -strlen($andor));
  951. }
  952. $fields_sql .= ' OR ';
  953. } // foreach($sites as $site_id)
  954. if ( ! empty($fields_sql))
  955. {
  956. $sql .= 'AND (' . substr($fields_sql, 0, -3) . ')';
  957. }
  958. }
  959. return $sql;
  960. }
  961. /**
  962. * Build SQL query
  963. */
  964. public function build_sql_query($qstring = '')
  965. {
  966. $entry_id = '';
  967. $year = '';
  968. $month = '';
  969. $day = '';
  970. $qtitle = '';
  971. $cat_id = '';
  972. $corder = array();
  973. $offset = 0;
  974. $page_marker = FALSE;
  975. $dynamic = TRUE;
  976. $this->pagination->dynamic_sql = TRUE;
  977. /**------
  978. /** Is dynamic='off' set?
  979. /**------*/
  980. // If so, we'll override all dynamically set variables
  981. if ($this->EE->TMPL->fetch_param('dynamic') == 'no')
  982. {
  983. $dynamic = FALSE;
  984. }
  985. /**------
  986. /** Do we allow dynamic POST variables to set parameters?
  987. /**------*/
  988. if ($this->EE->TMPL->fetch_param('dynamic_parameters') !== FALSE AND isset($_POST) AND count($_POST) > 0)
  989. {
  990. foreach (explode('|', $this->EE->TMPL->fetch_param('dynamic_parameters')) as $var)
  991. {
  992. if (isset($_POST[$var]) AND in_array($var, array('channel', 'entry_id', 'category', 'orderby', 'sort', 'sticky', 'show_future_entries', 'show_expired', 'entry_id_from', 'entry_id_to', 'not_entry_id', 'start_on', 'stop_before', 'year', 'month', 'day', 'display_by', 'limit', 'username', 'status', 'group_id', 'cat_limit', 'month_limit', 'offset', 'author_id')))
  993. {
  994. $this->EE->TMPL->tagparams[$var] = $_POST[$var];
  995. }
  996. if (isset($_POST[$var]) && strncmp($var, 'search:', 7) == 0)
  997. {
  998. $this->EE->TMPL->search_fields[substr($var, 7)] = $_POST[$var];
  999. }
  1000. }
  1001. }
  1002. /**------
  1003. /** Parse the URL query string
  1004. /**------*/
  1005. $this->uristr = $this->EE->uri->uri_string;
  1006. if ($qstring == '')
  1007. {
  1008. $qstring = $this->query_string;
  1009. }
  1010. $this->pagination->basepath = $this->EE->functions->create_url($this->uristr);
  1011. if ($qstring == '')
  1012. {
  1013. if ($this->EE->TMPL->fetch_param('require_entry') == 'yes')
  1014. {
  1015. return '';
  1016. }
  1017. }
  1018. else
  1019. {
  1020. /** --------------------------------------
  1021. /** Do we have a pure ID number?
  1022. /** --------------------------------------*/
  1023. if ($dynamic && is_numeric($qstring))
  1024. {
  1025. $entry_id = $qstring;
  1026. }
  1027. else
  1028. {
  1029. // Load the string helper
  1030. $this->EE->load->helper('string');
  1031. /** --------------------------------------
  1032. /** Parse day
  1033. /** --------------------------------------*/
  1034. if ($dynamic && preg_match("#(^|\/)(\d{4}/\d{2}/\d{2})#", $qstring, $match))
  1035. {
  1036. $ex = explode('/', $match[2]);
  1037. $year = $ex[0];
  1038. $month = $ex[1];
  1039. $day = $ex[2];
  1040. $qstring = trim_slashes(str_replace($match[0], '', $qstring));
  1041. }
  1042. /** --------------------------------------
  1043. /** Parse /year/month/
  1044. /** --------------------------------------*/
  1045. // added (^|\/) to make sure this doesn't trigger with url titles like big_party_2006
  1046. if ($dynamic && preg_match("#(^|\/)(\d{4}/\d{2})(\/|$)#", $qstring, $match))
  1047. {
  1048. $ex = explode('/', $match[2]);
  1049. $year = $ex[0];
  1050. $month = $ex[1];
  1051. $qstring = trim_slashes(str_replace($match[2], '', $qstring));
  1052. }
  1053. /** --------------------------------------
  1054. /** Parse ID indicator
  1055. /** --------------------------------------*/
  1056. if ($dynamic && preg_match("#^(\d+)(.*)#", $qstring, $match))
  1057. {
  1058. $seg = ( ! isset($match[2])) ? '' : $match[2];
  1059. if (substr($seg, 0, 1) == "/" OR $seg == '')
  1060. {
  1061. $entry_id = $match[1];
  1062. $qstring = trim_slashes(preg_replace("#^".$match[1]."#", '', $qstring));
  1063. }
  1064. }
  1065. /** --------------------------------------
  1066. /** Parse page number
  1067. /** --------------------------------------*/
  1068. if (($dynamic OR $this->EE->TMPL->fetch_param('paginate')) && preg_match("#^P(\d+)|/P(\d+)#", $qstring, $match))
  1069. {
  1070. $this->pagination->offset = (isset($match[2])) ? $match[2] : $match[1];
  1071. $this->pagination->basepath = $this->EE->functions->remove_double_slashes(str_replace($match[0], '', $this->pagination->basepath));
  1072. $this->uristr = $this->EE->functions->remove_double_slashes(str_replace($match[0], '', $this->uristr));
  1073. $qstring = trim_slashes(str_replace($match[0], '', $qstring));
  1074. $page_marker = TRUE;
  1075. }
  1076. /** --------------------------------------
  1077. /** Parse category indicator
  1078. /** --------------------------------------*/
  1079. // Text version of the category
  1080. if ($qstring != '' AND $this->reserved_cat_segment != '' AND in_array($this->reserved_cat_segment, explode("/", $qstring)) AND $dynamic AND $this->EE->TMPL->fetch_param('channel'))
  1081. {
  1082. $qstring = preg_replace("/(.*?)\/".preg_quote($this->reserved_cat_segment)."\//i", '', '/'.$qstring);
  1083. $sql = "SELECT DISTINCT cat_group FROM exp_channels WHERE site_id IN ('".implode("','", $this->EE->TMPL->site_ids)."') AND ";
  1084. $xsql = $this->EE->functions->sql_andor_string($this->EE->TMPL->fetch_param('channel'), 'channel_name');
  1085. if (substr($xsql, 0, 3) == 'AND') $xsql = substr($xsql, 3);
  1086. $sql .= ' '.$xsql;
  1087. $query = $this->EE->db->query($sql);
  1088. if ($query->num_rows() > 0)
  1089. {
  1090. $valid = 'y';
  1091. $valid_cats = explode('|', $query->row('cat_group') );
  1092. foreach($query->result_array() as $row)
  1093. {
  1094. if ($this->EE->TMPL->fetch_param('relaxed_categories') == 'yes')
  1095. {
  1096. $valid_cats = array_merge($valid_cats, explode('|', $row['cat_group']));
  1097. }
  1098. else
  1099. {
  1100. $valid_cats = array_intersect($valid_cats, explode('|', $row['cat_group']));
  1101. }
  1102. $valid_cats = array_unique($valid_cats);
  1103. if (count($valid_cats) == 0)
  1104. {
  1105. $valid = 'n';
  1106. break;
  1107. }
  1108. }
  1109. }
  1110. else
  1111. {
  1112. $valid = 'n';
  1113. }
  1114. if ($valid == 'y')
  1115. {
  1116. // the category URL title should be the first segment left at this point in $qstring,
  1117. // but because prior to this feature being added, category names were used in URLs,
  1118. // and '/' is a valid character for category names. If they have not updated their
  1119. // category url titles since updating to 1.6, their category URL title could still
  1120. // contain a '/'. So we'll try to get the category the correct way first, and if
  1121. // it fails, we'll try the whole $qstring
  1122. // do this as separate commands to work around a PHP 5.0.x bug
  1123. $arr = explode('/', $qstring);
  1124. $cut_qstring = array_shift($arr);
  1125. unset($arr);
  1126. $result = $this->EE->db->query("SELECT cat_id FROM exp_categories
  1127. WHERE cat_url_title='".$this->EE->db->escape_str($cut_qstring)."'
  1128. AND group_id IN ('".implode("','", $valid_cats)."')");
  1129. if ($result->num_rows() == 1)
  1130. {
  1131. $qstring = str_replace($cut_qstring, 'C'.$result->row('cat_id') , $qstring);
  1132. $cat_id = $result->row('cat_id');
  1133. }
  1134. else
  1135. {
  1136. // give it one more try using the whole $qstring
  1137. $result = $this->EE->db->query("SELECT cat_id FROM exp_categories
  1138. WHERE cat_url_title='".$this->EE->db->escape_str($qstring)."'
  1139. AND group_id IN ('".implode("','", $valid_cats)."')");
  1140. if ($result->num_rows() == 1)
  1141. {
  1142. $qstring = 'C'.$result->row('cat_id') ;
  1143. $cat_id = $result->row('cat_id');
  1144. }
  1145. }
  1146. }
  1147. }
  1148. // If we got here, category may be numeric
  1149. if (empty($cat_id))
  1150. {
  1151. $this->EE->load->helper('segment');
  1152. $cat_id = parse_category($this->query_string);
  1153. }
  1154. // If we were able to get a numeric category ID
  1155. if (is_numeric($cat_id) AND $cat_id !== FALSE)
  1156. {
  1157. $this->cat_request = TRUE;
  1158. }
  1159. // parse_category did not return a numberic ID, blow away $cat_id
  1160. else
  1161. {
  1162. $cat_id = FALSE;
  1163. }
  1164. /** --------------------------------------
  1165. /** Remove "N"
  1166. /** --------------------------------------*/
  1167. // The recent comments feature uses "N" as the URL indicator
  1168. // It needs to be removed if presenst
  1169. if (preg_match("#^N(\d+)|/N(\d+)#", $qstring, $match))
  1170. {
  1171. $this->uristr = $this->EE->functions->remove_double_slashes(str_replace($match[0], '', $this->uristr));
  1172. $qstring = trim_slashes(str_replace($match[0], '', $qstring));
  1173. }
  1174. /** --------------------------------------
  1175. /** Parse URL title
  1176. /** --------------------------------------*/
  1177. if (($cat_id == '' AND $year == '') OR $this->EE->TMPL->fetch_param('require_entry') == 'yes')
  1178. {
  1179. if (strpos($qstring, '/') !== FALSE)
  1180. {
  1181. $xe = explode('/', $qstring);
  1182. $qstring = current($xe);
  1183. }
  1184. if ($dynamic == TRUE)
  1185. {
  1186. $sql = "SELECT count(*) AS count
  1187. FROM exp_channel_titles, exp_channels
  1188. WHERE exp_channel_titles.channel_id = exp_channels.channel_id";
  1189. if ($entry_id != '')
  1190. {
  1191. $sql .= " AND exp_channel_titles.entry_id = '".$this->EE->db->escape_str($entry_id)."'";
  1192. }
  1193. else
  1194. {
  1195. $sql .= " AND exp_channel_titles.url_title = '".$this->EE->db->escape_str($qstring)."'";
  1196. }
  1197. $sql .= " AND exp_channels.site_id IN ('".implode("','", $this->EE->TMPL->site_ids)."') ";
  1198. $query = $this->EE->db->query($sql);
  1199. if ($query->row('count') == 0)
  1200. {
  1201. if ($this->EE->TMPL->fetch_param('require_entry') == 'yes')
  1202. {
  1203. return '';
  1204. }
  1205. $qtitle = '';
  1206. }
  1207. else
  1208. {
  1209. $qtitle = $qstring;
  1210. }
  1211. }
  1212. }
  1213. }
  1214. }
  1215. /**------
  1216. /** Entry ID number
  1217. /**------*/
  1218. // If the "entry ID" was hard-coded, use it instead of
  1219. // using the dynamically set one above
  1220. if ($this->EE->TMPL->fetch_param('entry_id'))
  1221. {
  1222. $entry_id = $this->EE->TMPL->fetch_param('entry_id');
  1223. }
  1224. /**------
  1225. /** Only Entries with Pages
  1226. /**------*/
  1227. if ($this->EE->TMPL->fetch_param('show_pages') !== FALSE && in_array($this->EE->TMPL->fetch_param('show_pages'), array('only', 'no')) && ($pages = $this->EE->config->item('site_pages')) !== FALSE)
  1228. {
  1229. $pages_uris = array();
  1230. foreach ($pages as $data)
  1231. {
  1232. $pages_uris += $data['uris'];
  1233. }
  1234. if (count($pages_uris) > 0 OR $this->EE->TMPL->fetch_param('show_pages') == 'only')
  1235. {
  1236. // consider entry_id
  1237. if ($this->EE->TMPL->fetch_param('entry_id') !== FALSE)
  1238. {
  1239. $not = FALSE;
  1240. if (strncmp($entry_id, 'not', 3) == 0)
  1241. {
  1242. $not = TRUE;
  1243. $entry_id = trim(substr($entry_id, 3));
  1244. }
  1245. $ids = explode('|', $entry_id);
  1246. if ($this->EE->TMPL->fetch_param('show_pages') == 'only')
  1247. {
  1248. if ($not === TRUE)
  1249. {
  1250. $entry_id = implode('|', array_diff(array_flip($pages_uris), explode('|', $ids)));
  1251. }
  1252. else
  1253. {
  1254. $entry_id = implode('|',array_diff($ids, array_diff($ids, array_flip($pages_uris))));
  1255. }
  1256. }
  1257. else
  1258. {
  1259. if ($not === TRUE)
  1260. {
  1261. $entry_id = "not {$entry_id}|".implode('|', array_flip($pages_uris));
  1262. }
  1263. else
  1264. {
  1265. $entry_id = implode('|',array_diff($ids, array_flip($pages_uris)));
  1266. }
  1267. }
  1268. }
  1269. else
  1270. {
  1271. $entry_id = (($this->EE->TMPL->fetch_param('show_pages') == 'no') ? 'not ' : '').implode('|', array_flip($pages_uris));
  1272. }
  1273. // No pages and show_pages only
  1274. if ($entry_id == '' && $this->EE->TMPL->fetch_param('show_pages') == 'only')
  1275. {
  1276. $this->sql = '';
  1277. return;
  1278. }
  1279. }
  1280. }
  1281. /**------
  1282. /** Assing the order variables
  1283. /**------*/
  1284. $order = $this->EE->TMPL->fetch_param('orderby');
  1285. $sort = $this->EE->TMPL->fetch_param('sort');
  1286. $sticky = $this->EE->TMPL->fetch_param('sticky');
  1287. /** -------------------------------------
  1288. /** Multiple Orders and Sorts...
  1289. /** -------------------------------------*/
  1290. if ($order !== FALSE && stristr($order, '|'))
  1291. {
  1292. $order_array = explode('|', $order);
  1293. if ($order_array[0] == 'random')
  1294. {
  1295. $order_array = array('random');
  1296. }
  1297. }
  1298. else
  1299. {
  1300. $order_array = array($order);
  1301. }
  1302. if ($sort !== FALSE && stristr($sort, '|'))
  1303. {
  1304. $sort_array = explode('|', $sort);
  1305. }
  1306. else
  1307. {
  1308. $sort_array = array($sort);
  1309. }
  1310. /** -------------------------------------
  1311. /** Validate Results for Later Processing
  1312. /** -------------------------------------*/
  1313. $base_orders = array('random', 'entry_id', 'date', 'entry_date', 'title', 'url_title', 'edit_date', 'comment_total', 'username', 'screen_name', 'most_recent_comment', 'expiration_date',
  1314. 'view_count_one', 'view_count_two', 'view_count_three', 'view_count_four');
  1315. foreach($order_array as $key => $order)
  1316. {
  1317. if ( ! in_array($order, $base_orders))
  1318. {
  1319. if (FALSE !== $order)
  1320. {
  1321. $set = 'n';
  1322. /** -------------------------------------
  1323. /** Site Namespace is Being Used, Parse Out
  1324. /** -------------------------------------*/
  1325. if (strpos($order, ':') !== FALSE)
  1326. {
  1327. $order_parts = explode(':', $order, 2);
  1328. if (isset($this->EE->TMPL->site_ids[$order_parts[0]]) && isset($this->cfields[$this->EE->TMPL->site_ids[$order_parts[0]]][$order_parts[1]]))
  1329. {
  1330. $corder[$key] = $this->cfields[$this->EE->TMPL->site_ids[$order_parts[0]]][$order_parts[1]];
  1331. $order_array[$key] = 'custom_field';
  1332. $set = 'y';
  1333. }
  1334. }
  1335. /** -------------------------------------
  1336. /** Find the Custom Field, Cycle Through All Sites for Tag
  1337. /** - If multiple sites have the same short_name for a field, we do a CONCAT ORDERBY in query
  1338. /** -------------------------------------*/
  1339. if ($set == 'n')
  1340. {
  1341. foreach($this->cfields as $site_id => $cfields)
  1342. {
  1343. // Only those sites specified
  1344. if ( ! in_array($site_id, $this->EE->TMPL->site_ids))
  1345. {
  1346. continue;
  1347. }
  1348. if (isset($cfields[$order]))
  1349. {
  1350. if ($set == 'y')
  1351. {
  1352. $corder[$key] .= '|'.$cfields[$order];
  1353. }
  1354. else
  1355. {
  1356. $corder[$key] = $cfields[$order];
  1357. $order_array[$key] = 'custom_field';
  1358. $set = 'y';
  1359. }
  1360. }
  1361. }
  1362. }
  1363. if ($set == 'n')
  1364. {
  1365. $order_array[$key] = FALSE;
  1366. }
  1367. }
  1368. }
  1369. if ( ! isset($sort_array[$key]))
  1370. {
  1371. $sort_array[$key] = 'desc';
  1372. }
  1373. }
  1374. foreach($sort_array as $key => $sort)
  1375. {
  1376. if ($sort == FALSE OR ($sort != 'asc' AND $sort != 'desc'))
  1377. {
  1378. $sort_array[$key] = "desc";
  1379. }
  1380. }
  1381. // fixed entry id ordering
  1382. if (($fixed_order = $this->EE->TMPL->fetch_param('fixed_order')) === FALSE OR preg_match('/[^0-9\|]/', $fixed_order))
  1383. {
  1384. $fixed_order = FALSE;
  1385. }
  1386. else
  1387. {
  1388. // MySQL will not order the entries correctly unless the results are constrained
  1389. // to matching rows only, so we force the entry_id as well
  1390. $entry_id = $fixed_order;
  1391. $fixed_order = preg_split('/\|/', $fixed_order, -1, PREG_SPLIT_NO_EMPTY);
  1392. // some peeps might want to be able to 'flip' it
  1393. // the default sort order is 'desc' but in this context 'desc' has a stronger "reversing"
  1394. // connotation, so we look not at the sort array, but the tag parameter itself, to see the user's intent
  1395. if ($sort == 'desc')
  1396. {
  1397. $fixed_order = array_reverse($fixed_order);
  1398. }
  1399. }
  1400. /**------
  1401. /** Build the master SQL query
  1402. /**------*/
  1403. $sql_a = "SELECT ";
  1404. $sql_b = ($this->EE->TMPL->fetch_param('category') OR $this->EE->TMPL->fetch_param('category_group') OR $cat_id != '' OR $order_array[0] == 'random') ? "DISTINCT(t.entry_id) " : "t.entry_id ";
  1405. if ($this->pagination->field_pagination == TRUE)
  1406. {
  1407. $sql_b .= ",wd.* ";
  1408. }
  1409. $sql_c = "COUNT(t.entry_id) AS count ";
  1410. $sql = "FROM exp_channel_titles AS t
  1411. LEFT JOIN exp_channels ON t.channel_id = exp_channels.channel_id ";
  1412. if ($this->pagination->field_pagination == TRUE)
  1413. {
  1414. $sql .= "LEFT JOIN exp_channel_data AS wd ON t.entry_id = wd.entry_id ";
  1415. }
  1416. elseif (in_array('custom_field', $order_array))
  1417. {
  1418. $sql .= "LEFT JOIN exp_channel_data AS wd ON t.entry_id = wd.entry_id ";
  1419. }
  1420. elseif ( ! empty($this->EE->TMPL->search_fields))
  1421. {
  1422. $sql .= "LEFT JOIN exp_channel_data AS wd ON wd.entry_id = t.entry_id ";
  1423. }
  1424. $sql .= "LEFT JOIN exp_members AS m ON m.member_id = t.author_id ";
  1425. if ($this->EE->TMPL->fetch_param('category') OR $this->EE->TMPL->fetch_param('category_group') OR ($cat_id != '' && $dynamic == TRUE))
  1426. {
  1427. /* --------------------------------
  1428. /* We use LEFT JOIN when there is a 'not' so that we get
  1429. /* entries that are not assigned to a category.
  1430. /* --------------------------------*/
  1431. if ((substr($this->EE->TMPL->fetch_param('category_group'), 0, 3) == 'not' OR substr($this->EE->TMPL->fetch_param('category'), 0, 3) == 'not') && $this->EE->TMPL->fetch_param('uncategorized_entries') !== 'no')
  1432. {
  1433. $sql .= "LEFT JOIN exp_category_posts ON t.entry_id = exp_category_posts.entry_id
  1434. LEFT JOIN exp_categories ON exp_category_posts.cat_id = exp_categories.cat_id ";
  1435. }
  1436. else
  1437. {
  1438. $sql .= "INNER JOIN exp_category_posts ON t.entry_id = exp_category_posts.entry_id
  1439. INNER JOIN exp_categories ON exp_category_pos

Large files files are truncated, but you can click here to view the full file