PageRenderTime 81ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 1ms

/cp/expressionengine/modules/channel/mod.channel.php

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

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