PageRenderTime 63ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/php/lib/mtdb.base.php

https://code.google.com/p/movabletype/
PHP | 1701 lines | 1561 code | 100 blank | 40 comment | 195 complexity | 9bdd2ffaf1370260fff7c08f70d576b9 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, LGPL-2.1

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

  1. <?php
  2. # Movable Type (r) Open Source (C) 2001-2010 Six Apart, Ltd.
  3. # This program is distributed under the terms of the
  4. # GNU General Public License, version 2.
  5. #
  6. # $Id: mtdb.base.php 5417 2010-05-12 02:58:17Z takayama $
  7. require_once('adodb-exceptions.inc.php');
  8. require_once('adodb.inc.php');
  9. require_once('adodb-active-record.inc.php');
  10. abstract class MTDatabase {
  11. var $savedqueries = array();
  12. // Member variables
  13. protected $id;
  14. protected $conn;
  15. protected $serializer;
  16. protected $pdo_enabled = false;
  17. protected $has_distinct = true;
  18. // Cache variables
  19. protected $_entry_id_cache = array();
  20. protected $_comment_count_cache = array();
  21. protected $_ping_count_cache = array();
  22. protected $_cat_id_cache = array();
  23. protected $_tag_id_cache = array();
  24. protected $_blog_id_cache = array();
  25. protected $_entry_link_cache = array();
  26. protected $_cat_link_cache = array();
  27. protected $_archive_link_cache = array();
  28. protected $_entry_tag_cache = array();
  29. protected $_blog_tag_cache = array();
  30. protected $_asset_tag_cache = array();
  31. protected $_blog_asset_tag_cache = array();
  32. protected $_author_id_cache = array();
  33. // Construction
  34. public function __construct($user, $password = '', $dbname = '', $host = '', $port = '', $sock = '') {
  35. $this->id = md5(uniqid('MTDatabase',true));
  36. $this->connect($user, $password, $dbname, $host, $port, $sock);
  37. ADOdb_Active_Record::SetDatabaseAdapter($this->conn);
  38. # $this->conn->debug = true;
  39. }
  40. // Abstract method
  41. abstract protected function connect($user, $password = '', $dbname = '', $host = '', $port = '', $sock = '');
  42. abstract public function escape($str);
  43. abstract public function set_names($mt);
  44. // Utility method
  45. public function has_distinct_support () {
  46. return $this->has_distinct;
  47. }
  48. public function db() {
  49. return $this->conn;
  50. }
  51. public function execute($sql) {
  52. return $this->conn->Execute($sql);
  53. }
  54. public function SelectLimit($sql, $limit = -1, $offset = -1) {
  55. return $this->conn->SelectLimit($sql, $limit, $offset);
  56. }
  57. public function unserialize($data) {
  58. if (!$this->serializer) {
  59. require_once("MTSerialize.php");
  60. $this->serializer = new MTSerialize();
  61. }
  62. return $this->serializer->unserialize($data);
  63. }
  64. public function serialize($data) {
  65. if (!$this->serializer) {
  66. require_once("MTSerialize.php");
  67. $this->serializer = new MTSerialize();
  68. }
  69. return $this->serializer->serialize($data);
  70. }
  71. protected function include_exclude_blogs(&$args) {
  72. if (isset($args['blog_ids']) || isset($args['include_blogs']) || isset($args['include_websites'])) {
  73. // The following are aliased
  74. $args['blog_ids'] and $args['include_blogs'] = $args['blog_ids'];
  75. $args['include_websites'] and $args['include_blogs'] = $args['include_websites'];
  76. $attr = $args['include_blogs'];
  77. unset($args['blog_ids']);
  78. unset($args['include_websites']);
  79. $is_excluded = 0;
  80. } elseif (isset($args['exclude_blogs']) || isset($args['exclude_websites'])) {
  81. $attr = $args['exclude_blogs'];
  82. $attr or $attr = $args['exclude_websites'];
  83. $is_excluded = 1;
  84. } elseif (isset($args['blog_id']) && is_numeric($args['blog_id'])) {
  85. return " = " . $args['blog_id'];
  86. } else {
  87. return;
  88. }
  89. if (preg_match('/-/', $attr)) {
  90. # parse range blog ids out
  91. $list = preg_split('/\s*,\s*/', $attr);
  92. $attr = '';
  93. foreach ($list as $item) {
  94. if (preg_match('/(\d+)-(\d+)/', $item, $matches)) {
  95. for ($i = $matches[1]; $i <= $matches[2]; $i++) {
  96. if ($attr != '') $attr .= ',';
  97. $attr .= $i;
  98. }
  99. } else {
  100. if ($attr != '') $attr .= ',';
  101. $attr .= $item;
  102. }
  103. }
  104. }
  105. $blog_ids = preg_split('/\s*,\s*/',
  106. $attr,
  107. -1, PREG_SPLIT_NO_EMPTY);
  108. $sql = '';
  109. if ($is_excluded) {
  110. $sql = 'not in ( ' . implode(',', $blog_ids) . ' )';
  111. } elseif ($args[include_blogs] == 'all') {
  112. $sql = '> 0';
  113. } elseif (
  114. ($args[include_blogs] == 'site')
  115. || ($args[include_blogs] == 'children')
  116. || ($args[include_blogs] == 'siblings')
  117. ) {
  118. $mt = MT::get_instance();
  119. $ctx = $mt->context();
  120. $blog = $ctx->stash('blog');
  121. if (!empty($blog) && $blog->class == 'blog') {
  122. require_once('class.mt_blog.php');
  123. $blog_class = new Blog();
  124. $blogs = $blog_class->Find("blog_parent_id = " . $blog->parent_id);
  125. $blog_ids = array();
  126. foreach($blogs as $b) {
  127. array_push($ids, $b->id);
  128. }
  129. if ( $args[include_with_website] )
  130. array_push($blog_ids, $blog->parent_id);
  131. if (count($blog_ids)) {
  132. $sql = 'in ( ' . implode(',', $blog_ids) . ' )';
  133. } else {
  134. $sql = '> 0';
  135. }
  136. } else {
  137. $sql = '> 0';
  138. }
  139. } else {
  140. if (count($blog_ids)) {
  141. $sql = 'in ( ' . implode(',', $blog_ids) . ' )';
  142. } else {
  143. $sql = '> 0';
  144. }
  145. }
  146. return $sql;
  147. }
  148. public function db2ts($dbts) {
  149. $dbts = preg_replace('/[^0-9]/', '', $dbts);
  150. return $dbts;
  151. }
  152. public function ts2db($ts) {
  153. preg_match('/^(\d\d\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?$/', $ts, $matches);
  154. list($all, $y, $mo, $d, $h, $m, $s) = $matches;
  155. return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $y, $mo, $d, $h, $m, $s);
  156. }
  157. public function apply_extract_date($part, $column) {
  158. return "extract($part from $column)";
  159. }
  160. // Deprecated method
  161. public function get_row($query = null, $output = OBJECT, $y = 0) {
  162. require_once('class.exception.php');
  163. throw new MTDeprecatedException('get_row was Deprecated.');
  164. }
  165. public function get_results($query = null, $output = ARRAY_A) {
  166. require_once('class.exception.php');
  167. throw new MTDeprecatedException('get_results was Deprecated.');
  168. }
  169. public function convert_fieldname($array) {
  170. require_once('class.exception.php');
  171. throw new MTDeprecatedException('convert_fieldname was Deprecated.');
  172. }
  173. public function expand_meta($rows) {
  174. require_once('class.exception.php');
  175. throw new MTDeprecatedException('expand_meta was Deprecated.');
  176. }
  177. public function get_meta($obj_type, $obj_id) {
  178. require_once('class.exception.php');
  179. throw new MTDeprecatedException('get_meta was Deprecated.');
  180. }
  181. function apply_limit_sql($sql, $limit, $offset = 0) {
  182. require_once('class.exception.php');
  183. throw new MTDeprecatedException('apply_limit_sql was Deprecated.');
  184. }
  185. // Public method
  186. public function resolve_url($path, $blog_id, $build_type = 3) {
  187. $path = preg_replace('!/$!', '', $path);
  188. $blog_id = intval($blog_id);
  189. # resolve for $path -- one of:
  190. # /path/to/file.html
  191. # /path/to/index.html
  192. # /path/to/
  193. # /path/to
  194. $mt = MT::get_instance();
  195. $index = $this->escape($mt->config('IndexBasename'));
  196. $escindex = $this->escape($index);
  197. require_once('class.mt_fileinfo.php');
  198. $records = null;
  199. $extras['join'] = array(
  200. 'mt_template' => array(
  201. 'condition' => "fileinfo_template_id = template_id"
  202. ),
  203. 'mt_templatemap' => array(
  204. 'condition' => "fileinfo_templatemap_id = templatemap_id",
  205. 'type' => 'left'
  206. ),
  207. );
  208. foreach ( array($path, urldecode($path), urlencode($path)) as $p ) {
  209. $where = "fileinfo_blog_id = $blog_id
  210. and ((fileinfo_url = '%1\$s' or fileinfo_url = '%1\$s/') or (fileinfo_url like '%1\$s/$escindex%%'))
  211. and template_type != 'backup'
  212. order by length(fileinfo_url) asc";
  213. $fileinfo= new FileInfo;
  214. $records = $fileinfo->Find(sprintf($where, $this->escape($p)), false, false, $extras);
  215. if (!empty($records))
  216. break;
  217. }
  218. $path = $p;
  219. if (empty($records)) return null;
  220. $found = false;
  221. foreach ($records as $record) {
  222. if ( !empty( $build_type ) ) {
  223. if ( !is_array( $build_type ) ) {
  224. $build_type_array = array( $build_type );
  225. } else {
  226. $build_type_array = $build_type;
  227. }
  228. $tmpl = $record->template();
  229. $map = $record->templatemap();
  230. $type = empty( $map ) ? $tmpl->build_type : $map->build_type;
  231. if ( !in_array( $type, $build_type_array ) ) {
  232. continue;
  233. }
  234. }
  235. $fiurl = $record->url;
  236. if ($fiurl == $path) {
  237. $found = true;
  238. break;
  239. }
  240. if ($fiurl == "$path/") {
  241. $found = true;
  242. break;
  243. }
  244. $ext = $record->blog()->file_extension;
  245. if (!empty($ext)) $ext = '.' . $ext;
  246. if ($fiurl == ($path.'/'.$index.$ext)) {
  247. $found = true; break;
  248. }
  249. if ($found) break;
  250. }
  251. if (!$found) return null;
  252. $blog = $record->blog();
  253. $this->_blog_id_cache[$blog->id] =& $blog;
  254. return $record;
  255. }
  256. public function load_index_template($ctx, $tmpl, $blog_id = null) {
  257. return $this->load_special_template($ctx, $tmpl, 'index', $blog_id);
  258. }
  259. public function fetch_websites($args) {
  260. $args['class'] = 'website';
  261. return $this->fetch_blogs($args);
  262. }
  263. public function fetch_blogs($args = null) {
  264. if ($blog_ids = $this->include_exclude_blogs($args))
  265. $blog_filter = 'blog_id ' . $blog_ids;
  266. else
  267. $blog_filter = '1 = 1';
  268. if (!isset($args['class']))
  269. $args['class'] = 'blog';
  270. $where = $blog_filter;
  271. $where .= $args['class'] == '*' ? "" : " and blog_class = '".$args['class']."'";
  272. $where .= ' order by blog_name';
  273. require_once('class.mt_blog.php');
  274. $blogs = null;
  275. $blog = new Blog();
  276. $blogs = $blog->Find($where);
  277. return $blogs;
  278. }
  279. public function fetch_templates($args = null) {
  280. if (isset($args['type'])) {
  281. $type_filter = 'and template_type = \'' . $this->escape($args['type']) . '\'';
  282. }
  283. if (isset($args['blog_id'])) {
  284. $blog_filter = 'and template_blog_id = ' . intval($args['blog_id']);
  285. }
  286. $where = "1 = 1
  287. $blog_filter
  288. $type_filter
  289. order by template_name";
  290. require_once('class.mt_template.php');
  291. $template = new Template;
  292. $result = $template->Find($where);
  293. return $result;
  294. }
  295. public function fetch_templatemap($args = null) {
  296. if (isset($args['type'])) {
  297. $type_filter = 'and templatemap_archive_type = \'' . $this->escape($args['type']) . '\'';
  298. }
  299. if (isset($args['blog_id'])) {
  300. $blog_filter = 'and templatemap_blog_id = ' . intval($args['blog_id']);
  301. }
  302. $where = "1 = 1
  303. $blog_filter
  304. $type_filter
  305. order by templatemap_archive_type";
  306. require_once('class.mt_templatemap.php');
  307. $tmap = new TemplateMap;
  308. $result = $tmap->Find($where);
  309. return $result;
  310. }
  311. public function load_special_template($ctx, $tmpl, $type, $blog_id = null) {
  312. if (empty($blog_id))
  313. $blog_id = $ctx->stash('blog_id');
  314. $tmpl_name = $this->escape($tmpl);
  315. $where = "template_blog_id = $blog_id";
  316. if (!empty($tmpl)) {
  317. $where .= " and (template_name = '$tmpl_name'
  318. or template_outfile = '$tmpl_name'
  319. or template_identifier='$tmpl_name')";
  320. }
  321. $where .= " and template_type = '".$this->escape($type)."'";
  322. require_once('class.mt_template.php');
  323. $template = new Template;
  324. $template->Load($where);
  325. return $template;
  326. }
  327. public function fetch_config() {
  328. require_once('class.mt_config.php');
  329. $config = new Config;
  330. $config->Load();
  331. return $config;
  332. }
  333. public function category_link($cid) {
  334. if (isset($this->_cat_link_cache[$cid])) {
  335. $url = $this->_cat_link_cache[$cid];
  336. } else {
  337. $where = "fileinfo_category_id = $cid and
  338. fileinfo_archive_type = 'Category'";
  339. require_once('class.mt_fileinfo.php');
  340. $finfo = new FileInfo;
  341. $finfos = $finfo->Find($where);
  342. $found = false;
  343. foreach($finfos as $fi) {
  344. $tmap = $fi->TemplateMap();
  345. if ($tmap->is_preferred == 1) {
  346. $found = true;
  347. $finfo = $fi;
  348. break;
  349. }
  350. }
  351. if (!$found)
  352. return null;
  353. $blog = $finfo->Blog();
  354. $blog_url = $blog->archive_url();
  355. if (empty($blog_url))
  356. $blog_url = $blog->site_url();
  357. $blog_url = preg_replace('!(https?://(?:[^/]+))/.*!', '$1', $blog_url);
  358. $url = $blog_url . $finfo->url;
  359. require_once('MTUtil.php');
  360. $url = _strip_index($url, $blog);
  361. $this->_cat_link_cache[$cid] = $url;
  362. }
  363. return $url;
  364. }
  365. public function archive_link($ts, $at, $sql, $args) {
  366. $blog_id = intval($args['blog_id']);
  367. if (isset($this->_archive_link_cache[$blog_id.';'.$ts.';'.$at])) {
  368. $url = $this->_archive_link_cache[$blog_id.';'.$ts.';'.$at];
  369. } else {
  370. if (empty($sql)) {
  371. $sql = "fileinfo_startdate = '$ts'
  372. and fileinfo_blog_id = $blog_id
  373. and fileinfo_archive_type = '" . $this->escape($at). "'" .
  374. " and templatemap_is_preferred = 1";
  375. }
  376. $extras['join'] = array(
  377. 'mt_templatemap' => array(
  378. 'condition' => "templatemap_id = fileinfo_templatemap_id"
  379. )
  380. );
  381. require_once('class.mt_fileinfo.php');
  382. $finfo = new FileInfo;
  383. $infos = $finfo->Find($sql, false, false, $extras);
  384. if (empty($infos))
  385. return null;
  386. $finfo = $infos[0];
  387. $blog = $finfo->blog();
  388. if ($at == 'Page') {
  389. $blog_url = $blog->site_url();
  390. } else {
  391. $blog_url = $blog->archive_url();
  392. }
  393. require_once('MTUtil.php');
  394. $blog_url = preg_replace('!(https?://(?:[^/]+))/.*!', '$1', $blog_url);
  395. $url = $blog_url . $finfo->fileinfo_url;
  396. $url = _strip_index($url, $blog);
  397. $this->_archive_link_cache[$ts.';'.$at] = $url;
  398. }
  399. return $url;
  400. }
  401. public function entry_link($eid, $at = "Individual", $args = null) {
  402. $eid = intval($eid);
  403. if (isset($this->_entry_link_cache[$eid.';'.$at])) {
  404. $url = $this->_entry_link_cache[$eid.';'.$at];
  405. } else {
  406. $extras['join'] = array(
  407. 'mt_templatemap' => array(
  408. 'condition' => "templatemap_id = fileinfo_templatemap_id"
  409. )
  410. );
  411. $filter = '';
  412. if (preg_match('/Category/', $at)) {
  413. $extras['join']['mt_placement'] = array(
  414. 'condition' => "fileinfo_category_id = placement_category_id"
  415. );
  416. $filter = " and placement_entry_id = $eid
  417. and placement_is_primary = 1";
  418. }
  419. if (preg_match('/Page/', $at)) {
  420. $entry = $this->fetch_page($eid);
  421. } else {
  422. $entry = $this->fetch_entry($eid);
  423. }
  424. $ts = $entry->entry_authored_on;
  425. if (preg_match('/Monthly$/', $at)) {
  426. $ts = substr($ts, 0, 6) . '01000000';
  427. } elseif (preg_match('/Daily$/', $at)) {
  428. $ts = substr($ts, 0, 8) . '000000';
  429. } elseif (preg_match('/Weekly$/', $at)) {
  430. require_once("MTUtil.php");
  431. list($ws, $we) = start_end_week($ts);
  432. $ts = $ws;
  433. } elseif (preg_match('/Yearly$/', $at)) {
  434. $ts = substr($ts, 0, 4) . '0101000000';
  435. } elseif ($at == 'Individual' || $at == 'Page') {
  436. $filter .= " and fileinfo_entry_id = $eid";
  437. }
  438. if ($ts != $entry->entry_authored_on) {
  439. $filter .= " and fileinfo_startdate = '$ts'";
  440. }
  441. if (preg_match('/Author/', $at)) {
  442. $filter .= " and fileinfo_author_id = ". $entry->entry_author_id;
  443. }
  444. $where .= "templatemap_archive_type = '$at'
  445. and templatemap_is_preferred = 1
  446. $filter";
  447. if (isset($args['blog_id']))
  448. $where .= " and fileinfo_blog_id = " . $args['blog_id'];
  449. require_once('class.mt_fileinfo.php');
  450. $finfo = new FileInfo;
  451. $infos = $finfo->Find($where, false, false, $extras);
  452. if (empty($infos))
  453. return null;
  454. $finfo = $infos[0];
  455. $blog = $finfo->blog();
  456. if ($at == 'Page') {
  457. $blog_url = $blog->site_url();
  458. } else {
  459. $blog_url = $blog->archive_url();
  460. if (empty($blog_url))
  461. $blog_url = $blog->site_url();
  462. }
  463. require_once('MTUtil.php');
  464. $blog_url = preg_replace('!(https?://(?:[^/]+))/.*!', '$1', $blog_url);
  465. $url = $blog_url . $finfo->fileinfo_url;
  466. $url = _strip_index($url, $blog);
  467. $this->_entry_link_cache[$eid.';'.$at] = $url;
  468. }
  469. if ($at != 'Individual' && $at != 'Page') {
  470. if (!$args || !isset($args['no_anchor'])) {
  471. $url .= '#' . (!$args || isset($args['valid_html']) ? 'a' : '') .
  472. sprintf("%06d", $eid);
  473. }
  474. }
  475. return $url;
  476. }
  477. public function get_template_text($ctx, $module, $blog_id = null, $type = 'custom', $global = null) {
  478. if (empty($blog_id))
  479. $blog_id = $ctx->stash('blog_id');
  480. if ($type === 'custom' || $type === 'widget'|| $type === 'widgetset') {
  481. $col = 'template_name';
  482. $type_filter = "and template_type='$type'";
  483. } else {
  484. $col = 'template_identifier';
  485. $type_filter = "";
  486. }
  487. if (!isset($global)) {
  488. $blog_filter = "template_blog_id in (".$this->escape($blog_id).",0)";
  489. } elseif ($global) {
  490. $blog_filter = "template_blog_id = 0";
  491. } else {
  492. $blog_filter = "template_blog_id = ".$this->escape($blog_id);
  493. }
  494. require_once('class.mt_template.php');
  495. $template = new Template;
  496. $where = "$blog_filter
  497. and $col = '".$this->escape($module)."'
  498. $type_filter
  499. order by template_blog_id desc";
  500. $tmpls = $template->Find($where);
  501. if (empty($tmpls)) return '';
  502. $tmpl = $tmpls[0]->text;
  503. $ts = $tmpls[0]->modified_on;
  504. $file = $tmpls[0]->linked_file;
  505. $mtime = $tmpls[0]->linked_file_mtime;
  506. $size = $tmpls[0]->linked_file_size;
  507. if ($file) {
  508. if (!file_exists($file)) {
  509. $blog = $ctx->stash('blog');
  510. if ($blog->id != $blog_id) {
  511. $blog = $this->fetch_blog($blog_id);
  512. }
  513. $path = $blog->site_path();
  514. if (!preg_match('![\\/]$!', $path))
  515. $path .= '/';
  516. $path .= $file;
  517. if (is_file($path) && is_readable($path))
  518. $file = $path;
  519. else
  520. $file = '';
  521. }
  522. if ($file) {
  523. if ((filemtime($file) > $mtime) || (filesize($file) != $size)) {
  524. $contents = @file($file);
  525. $tmpl = implode('', $contents);
  526. }
  527. }
  528. }
  529. return $tmpl;
  530. }
  531. public function fetch_website($blog_id) {
  532. if (!empty($this->_blog_id_cache) && isset($this->_blog_id_cache[$blog_id])) {
  533. return $this->_blog_id_cache[$blog_id];
  534. }
  535. require_once('class.mt_website.php');
  536. $blog = new Website;
  537. $blog->Load("blog_id = $blog_id");
  538. $this->_blog_id_cache[$blog_id] = $blog;
  539. return $blog;
  540. }
  541. public function fetch_blog($blog_id) {
  542. if (!empty($this->_blog_id_cache) && isset($this->_blog_id_cache[$blog_id])) {
  543. return $this->_blog_id_cache[$blog_id];
  544. }
  545. require_once('class.mt_blog.php');
  546. $blog = new Blog;
  547. $blog->Load("blog_id = $blog_id");
  548. $this->_blog_id_cache[$blog_id] = $blog;
  549. return $blog;
  550. }
  551. function fetch_pages($args) {
  552. $args['class'] = 'page';
  553. return $this->fetch_entries($args);
  554. }
  555. function fetch_entry($eid) {
  556. $args['class'] = 'entry';
  557. $args['entry_id'] = $eid;
  558. $entries = $this->fetch_entries($args);
  559. if (empty($entries))
  560. return null;
  561. else
  562. return $entries[0];
  563. }
  564. public function fetch_entries($args, &$total_count = NULL) {
  565. require_once('class.mt_entry.php');
  566. $extras = array();
  567. if ($sql = $this->include_exclude_blogs($args)) {
  568. $blog_filter = 'and entry_blog_id ' . $sql;
  569. } elseif (isset($args['blog_id'])) {
  570. $blog_id = intval($args['blog_id']);
  571. $blog_filter = 'and entry_blog_id = ' . $blog_id;
  572. $blog = $this->fetch_blog($blog_id);
  573. }
  574. if ( empty($blog) ) {
  575. $mt = MT::get_instance();
  576. $blog = $this->fetch_blog($mt->blog_id());
  577. }
  578. if (empty($blog))
  579. return null;
  580. // determine any custom fields that we should filter on
  581. $fields = array();
  582. foreach ($args as $name => $v)
  583. if (preg_match('/^field___(\w+)$/', $name, $m))
  584. $fields[$m[1]] = $v;
  585. # automatically include offset if in request
  586. if ($args['offset'] == 'auto') {
  587. $args['offset'] = 0;
  588. if ($args['limit'] || $args['lastn']) {
  589. if (intval($_REQUEST['offset']) > 0) {
  590. $args['offset'] = intval($_REQUEST['offset']);
  591. }
  592. }
  593. }
  594. if ($args['limit'] > 0) {
  595. $args['lastn'] = $args['limit'];
  596. } elseif (!isset($args['days']) && !isset($args['lastn'])) {
  597. # if ($days = $blog['blog_days_on_index']) {
  598. # if (!isset($args['recently_commented_on'])) {
  599. # $args['days'] = $days;
  600. # }
  601. # } elseif ($posts = $blog['blog_entries_on_index']) {
  602. # $args['lastn'] = $posts;
  603. # }
  604. }
  605. if ($args['limit'] == 'auto') {
  606. if ((intval($_REQUEST['limit']) > 0) && (intval($_REQUEST['limit']) < $args['lastn'])) {
  607. $args['lastn'] = intval($_REQUEST['limit']);
  608. } elseif (!isset($args['days']) && !isset($args['lastn'])) {
  609. if ($days = $blog->blog_days_on_index) {
  610. if (!isset($args['recently_commented_on'])) {
  611. $args['days'] = $days;
  612. }
  613. } elseif ($posts = $blog->blog_entries_on_index) {
  614. $args['lastn'] = $posts;
  615. }
  616. }
  617. }
  618. if (isset($args['include_blogs']) or isset($args['exclude_blogs'])) {
  619. $blog_ctx_arg = isset($args['include_blogs']) ?
  620. array('include_blogs' => $args['include_blogs']) :
  621. array('exclude_blogs' => $args['exclude_blogs']);
  622. }
  623. # a context hash for filter routines
  624. $ctx = array();
  625. $filters = array();
  626. if (!isset($_REQUEST['entry_ids_published'])) {
  627. $_REQUEST['entry_ids_published'] = array();
  628. }
  629. if (isset($args['unique']) && $args['unique']) {
  630. $filters[] = create_function('$e,$ctx', 'return !isset($ctx["entry_ids_published"][$e->entry_id]);');
  631. $ctx['entry_ids_published'] = &$_REQUEST['entry_ids_published'];
  632. }
  633. # special case for selecting a particular entry
  634. if (isset($args['entry_id'])) {
  635. $entry_filter = 'and entry_id = '.$args['entry_id'];
  636. $start = ''; $end = ''; $limit = 1; $blog_filter = ''; $day_filter = '';
  637. } else {
  638. $entry_filter = '';
  639. }
  640. # special case for excluding a particular entry
  641. if (isset($args['not_entry_id'])) {
  642. $entry_filter .= ' and entry_id != '.$args['not_entry_id'];
  643. }
  644. $entry_list = array();
  645. # Adds a category filter to the filters list.
  646. $cat_class = 'category';
  647. if (!isset($args['class']))
  648. $args['class'] = 'entry';
  649. if ($args['class'] == 'page')
  650. $cat_class = 'folder';
  651. if (isset($args['category']) or isset($args['categories'])) {
  652. $category_arg = isset($args['category']) ? $args['category'] : $args['categories'];
  653. require_once("MTUtil.php");
  654. if (!preg_match('/\b(AND|OR|NOT)\b|\(|\)/i', $category_arg)) {
  655. $not_clause = false;
  656. $cats = cat_path_to_category($category_arg, $blog_ctx_arg, $cat_class);
  657. if (!empty($cats)) {
  658. $category_arg = '';
  659. foreach ($cats as $cat) {
  660. if ($category_arg != '')
  661. $category_arg .= '|| ';
  662. $category_arg .= '#' . $cat->category_id;
  663. }
  664. $category_arg = '(' . $category_arg . ')';
  665. }
  666. } else {
  667. $not_clause = preg_match('/\bNOT\b/i', $category_arg);
  668. if ($blog_ctx_arg)
  669. $cats =& $this->fetch_categories(array_merge($blog_ctx_arg, array('show_empty' => 1, 'class' => $cat_class)));
  670. else
  671. $cats =& $this->fetch_categories(array('blog_id' => $blog_id, 'show_empty' => 1, 'class' => $cat_class));
  672. }
  673. if (!empty($cats)) {
  674. $cexpr = create_cat_expr_function($category_arg, $cats, array('children' => $args['include_subcategories']));
  675. if ($cexpr) {
  676. $cmap = array();
  677. $cat_list = array();
  678. foreach ($cats as $cat)
  679. $cat_list[] = $cat->category_id;
  680. $pl = $this->fetch_placements(array('category_id' => $cat_list));
  681. if (!empty($pl)) {
  682. foreach ($pl as $p) {
  683. $cmap[$p->placement_entry_id][$p->placement_category_id]++;
  684. if (!$not_clause)
  685. $entry_list[$p->placement_entry_id] = 1;
  686. }
  687. }
  688. $ctx['p'] =& $cmap;
  689. $filters[] = $cexpr;
  690. } else {
  691. return null;
  692. }
  693. }
  694. } elseif (isset($args['category_id'])) {
  695. require_once("MTUtil.php");
  696. $cat = $this->fetch_category($args['category_id']);
  697. if (!empty($cat)) {
  698. $cats = array($cat);
  699. $cmap = array();
  700. $cexpr = create_cat_expr_function($cat->category_label, $cats, array('children' => $args['include_subcategories']));
  701. $pl = $this->fetch_placements(array('category_id' => array($cat->category_id)));
  702. if (!empty($pl)) {
  703. foreach ($pl as $p) {
  704. $cmap[$p->placement_entry_id][$p->placement_category_id]++;
  705. }
  706. $ctx['p'] =& $cmap;
  707. $filters[] = $cexpr;
  708. } else {
  709. # this category have no entries (or pages)
  710. return null;
  711. }
  712. }
  713. }
  714. if ((0 == count($filters)) && (isset($args['show_empty']) && (1 == $args['show_empty']))) {
  715. return null;
  716. }
  717. # Adds a tag filter to the filters list.
  718. if (isset($args['tags']) or isset($args['tag'])) {
  719. $tag_arg = isset($args['tag']) ? $args['tag'] : $args['tags'];
  720. require_once("MTUtil.php");
  721. $not_clause = preg_match('/\bNOT\b/i', $tag_arg);
  722. $include_private = 0;
  723. $tag_array = tag_split($tag_arg);
  724. foreach ($tag_array as $tag) {
  725. if ($tag && (substr($tag,0,1) == '@')) {
  726. $include_private = 1;
  727. }
  728. }
  729. if (isset($blog_ctx_arg))
  730. $tags = $this->fetch_entry_tags(array($blog_ctx_arg, 'tag' => $tag_arg, 'include_private' => $include_private, 'class' => $args['class']));
  731. else
  732. $tags = $this->fetch_entry_tags(array('blog_id' => $blog_id, 'tag' => $tag_arg, 'include_private' => $include_private, 'class' => $args['class']));
  733. if (!is_array($tags)) $tags = array();
  734. $cexpr = create_tag_expr_function($tag_arg, $tags);
  735. if ($cexpr) {
  736. $tmap = array();
  737. $tag_list = array();
  738. foreach ($tags as $tag) {
  739. $tag_list[] = $tag->tag_id;
  740. }
  741. if (isset($blog_ctx_arg))
  742. $ot = $this->fetch_objecttags(array('tag_id' => $tag_list, 'datasource' => 'entry', $blog_ctx_arg));
  743. elseif ($args['blog_id'])
  744. $ot = $this->fetch_objecttags(array('tag_id' => $tag_list, 'datasource' => 'entry', 'blog_id' => intval($args['blog_id'])));
  745. if ($ot) {
  746. foreach ($ot as $o) {
  747. $tmap[$o->objecttag_object_id][$o->objecttag_tag_id]++;
  748. if (!$not_clause)
  749. $entry_list[$o->objecttag_object_id] = 1;
  750. }
  751. }
  752. $ctx['t'] =& $tmap;
  753. $filters[] = $cexpr;
  754. } else {
  755. return null;
  756. }
  757. }
  758. # Adds a score or rate filter to the filters list.
  759. if (isset($args['namespace'])) {
  760. require_once("MTUtil.php");
  761. $arg_names = array('min_score', 'max_score', 'min_rate', 'max_rate', 'min_count', 'max_count' );
  762. foreach ($arg_names as $n) {
  763. if (isset($args[$n])) {
  764. $rating_args = $args[$n];
  765. $cexpr = create_rating_expr_function($rating_args, $n, $args['namespace']);
  766. if ($cexpr) {
  767. $filters[] = $cexpr;
  768. } else {
  769. return null;
  770. }
  771. }
  772. }
  773. if (isset($args['scored_by'])) {
  774. $voter = $this->fetch_author_by_name($args['scored_by']);
  775. if (empty($voter)) {
  776. echo "Invalid scored by filter: ".$args['scored_by'];
  777. return null;
  778. }
  779. $cexpr = create_rating_expr_function($voter->author_id, 'scored_by', $args['namespace']);
  780. if ($cexpr) {
  781. $filters[] = $cexpr;
  782. } else {
  783. return null;
  784. }
  785. }
  786. }
  787. # Adds an count of comment filter
  788. if (isset($args['max_comment']) && is_numeric($args['max_comment'])) {
  789. $max_comment_filter = 'and entry_comment_count <= ' . intval($args['max_comment']);
  790. }
  791. if (isset($args['min_comment']) && is_numeric($args['min_comment'])) {
  792. $min_comment_filter = 'and entry_comment_count >= ' . intval($args['min_comment']);
  793. }
  794. if (count($entry_list) && ($entry_filter == '')) {
  795. $entry_list = implode(",", array_keys($entry_list));
  796. # set a reasonable cap on the entry list cache. if
  797. # user is selecting something too big, then they'll
  798. # just have to wait through a scan.
  799. if (strlen($entry_list) < 2048)
  800. $entry_filter = "and entry_id in ($entry_list)";
  801. }
  802. if (isset($args['author'])) {
  803. $author_filter = 'and author_name = \'' .
  804. $this->escape($args['author']) . "'";
  805. $extras['join']['mt_author'] = array(
  806. 'condition' => "entry_author_id = author_id"
  807. );
  808. }
  809. $start = isset($args['current_timestamp'])
  810. ? $args['current_timestamp'] : null;
  811. $end = isset($args['current_timestamp_end'])
  812. ? $args['current_timestamp_end'] : null;
  813. if ($start and $end) {
  814. $start = $this->ts2db($start);
  815. $end = $this->ts2db($end);
  816. $date_filter = "and entry_authored_on between '$start' and '$end'";
  817. } elseif ($start) {
  818. $start = $this->ts2db($start);
  819. $date_filter = "and entry_authored_on >= '$start'";
  820. } elseif ($end) {
  821. $end = $this->ts2db($end);
  822. $date_filter = "and entry_authored_on <= '$end'";
  823. } else {
  824. $date_filter = '';
  825. }
  826. if (isset($args['lastn'])) {
  827. if (!isset($args['entry_id'])) $limit = $args['lastn'];
  828. } elseif (isset($args['days']) && !$date_filter) {
  829. $day_filter = 'and ' . $this->limit_by_day_sql('entry_authored_on', intval($args['days']));
  830. } else {
  831. $found_valid_args = 0;
  832. foreach ( array(
  833. 'lastn', 'days',
  834. 'category', 'categories', 'category_id',
  835. 'tag', 'tags',
  836. 'author',
  837. 'min_score', 'max_score',
  838. 'min_rate', 'max_rate',
  839. 'min_count', 'max_count',
  840. 'min_comment', 'max_comment'
  841. ) as $valid_key )
  842. {
  843. if (array_key_exists($valid_key, $args)) {
  844. $found_valid_args = 1;
  845. break;
  846. }
  847. }
  848. if ((!isset($args['current_timestamp']) &&
  849. !isset($args['current_timestamp_end'])) &&
  850. ($limit <= 0) &&
  851. (!$found_valid_args) &&
  852. (isset($blog))) {
  853. if ($days = $blog->blog_days_on_index) {
  854. if (!isset($args['recently_commented_on'])) {
  855. $day_filter = 'and ' . $this->limit_by_day_sql('entry_authored_on', $days);
  856. }
  857. } elseif ($posts = $blog->blog_entries_on_index) {
  858. $limit = $posts;
  859. }
  860. }
  861. }
  862. if (isset($args['sort_order'])) {
  863. if ($args['sort_order'] == 'ascend') {
  864. $order = 'asc';
  865. } elseif ($args['sort_order'] == 'descend') {
  866. $order = 'desc';
  867. }
  868. }
  869. if (!isset($order)) {
  870. $order = 'desc';
  871. if (isset($blog) && isset($blog->blog_sort_order_posts)) {
  872. if ($blog->blog_sort_order_posts == 'ascend') {
  873. $order = 'asc';
  874. }
  875. }
  876. }
  877. if (isset($args['class'])) {
  878. $class = $this->escape($args['class']);
  879. } else {
  880. $class = 'entry';
  881. }
  882. $class_filter = "and entry_class='$class'";
  883. if ($args['class'] == '*') $class_filter = '';
  884. if ( isset($args['sort_by'])
  885. && (($args['sort_by'] == 'score') || ($args['sort_by'] == 'rate'))) {
  886. $extras['join'] = array(
  887. 'mt_objectscore' => array(
  888. 'type' => 'left',
  889. 'condition' => "objectscore_object_id = entry_id and objectscore_namespace='".
  890. $args['namespace']."' and objectscore_object_ds='".$class."'"
  891. )
  892. );
  893. $extras['distinct'] = 1;
  894. }
  895. if (isset($args['offset']))
  896. $offset = $args['offset'];
  897. if (isset($args['limit'])) {
  898. if (isset($args['sort_by'])) {
  899. if ($args['sort_by'] == 'title') {
  900. $sort_field = 'entry_title';
  901. } elseif ($args['sort_by'] == 'id') {
  902. $sort_field = 'entry_id';
  903. } elseif ($args['sort_by'] == 'status') {
  904. $sort_field = 'entry_status';
  905. } elseif ($args['sort_by'] == 'modified_on') {
  906. $sort_field = 'entry_modified_on';
  907. } elseif ($args['sort_by'] == 'author_id') {
  908. $sort_field = 'entry_author_id';
  909. } elseif ($args['sort_by'] == 'excerpt') {
  910. $sort_field = 'entry_excerpt';
  911. } elseif ($args['sort_by'] == 'comment_created_on') {
  912. $sort_field = $args['sort_by'];
  913. } elseif ($args['sort_by'] == 'trackback_count') {
  914. $sort_field = 'entry_ping_count';
  915. } elseif (preg_match('/field[:\.]/', $args['sort_by'])) {
  916. $no_resort = 0;
  917. } else {
  918. $sort_field = 'entry_' . $args['sort_by'];
  919. }
  920. if ($sort_field) $no_resort = 1;
  921. if ($args['sort_by'] == 'score' || $args['sort_by'] == 'rate') {
  922. $post_sort_limit = $limit;
  923. $post_sort_offset = $offset;
  924. $limit = 0; $offset = 0;
  925. $no_resort = 0;
  926. $sort_field = "entry_modified_on";
  927. }
  928. }
  929. else {
  930. $sort_field ='entry_authored_on';
  931. }
  932. if ($sort_field) {
  933. $base_order = ($args['sort_order'] == 'ascend' ? 'asc' : 'desc');
  934. $base_order or $base_order = 'desc';
  935. }
  936. } else {
  937. $base_order = 'desc';
  938. if (isset($args['base_sort_order'])) {
  939. if ($args['base_sort_order'] == 'ascend')
  940. $base_order = 'asc';
  941. }
  942. $sort_field ='entry_authored_on';
  943. $no_resort = 0;
  944. }
  945. if (count($filters) || !is_null($total_count)) {
  946. $post_select_limit = $limit;
  947. $post_select_offset = $offset;
  948. $limit = 0; $offset = 0;
  949. }
  950. if (count($fields)) {
  951. $meta_join_num = 1;
  952. $entry_meta_info = Entry::get_meta_info();
  953. if (!empty($entry_meta_info)) {
  954. foreach ($fields as $name => $value) {
  955. if (isset($entry_meta_info[$name])) {
  956. $meta_col = $entry_meta_info[$name];
  957. $value = $this->escape($value);
  958. $table = "mt_entry_meta entry_meta$meta_join_num";
  959. $extras['join'][$table] = array(
  960. 'condition' => "(entry_meta$meta_join_num.entry_meta_entry_id = entry_id
  961. and entry_meta$meta_join_num.entry_meta_type = 'field.$name'
  962. and entry_meta$meta_join_num.entry_meta_$meta_col='$value')\n"
  963. );
  964. $meta_join_num++;
  965. }
  966. }
  967. }
  968. }
  969. $join_clause = '';
  970. if (isset($extras['join'])) {
  971. $joins = $extras['join'];
  972. $keys = array_keys($joins);
  973. foreach($keys as $key) {
  974. $table = $key;
  975. $cond = $joins[$key]['condition'];
  976. $type = '';
  977. if (isset($joins[$key]['type']))
  978. $type = $joins[$key]['type'];
  979. $join_clause .= ' ' . strtolower($type) . ' JOIN ' . $table . ' ON ' . $cond;
  980. }
  981. }
  982. $sql = "select
  983. mt_entry.*
  984. from mt_entry
  985. $join_clause
  986. where
  987. entry_status = 2
  988. $blog_filter
  989. $entry_filter
  990. $author_filter
  991. $date_filter
  992. $day_filter
  993. $class_filter
  994. $max_comment_filter
  995. $min_comment_filter";
  996. if ($sort_field)
  997. $sql .= "order by $sort_field $base_order";
  998. if (isset($args['recently_commented_on'])) {
  999. $rco = $args['recently_commented_on'];
  1000. $sql = $this->entries_recently_commented_on_sql($sql);
  1001. $args['sort_by'] or $args['sort_by'] = 'comment_created_on';
  1002. $args['sort_order'] or $args['sort_order'] = 'descend';
  1003. $post_select_limit = $rco;
  1004. $no_resort = 1;
  1005. } elseif ( !is_null($total_count) ) {
  1006. $orig_offset = $post_select_offset ? $post_select_offset : $offset;
  1007. $orig_limit = $post_select_limit ? $post_select_limit : $limit;
  1008. }
  1009. if ($limit <= 0) $limit = -1;
  1010. if ($offset <= 0) $offset = -1;
  1011. $result = $this->db()->SelectLimit($sql, $limit, $offset);
  1012. if ($result->EOF) return null;
  1013. $field_names = array_keys($result->fields);
  1014. $entries = array();
  1015. $j = 0;
  1016. $offset = $post_select_offset ? $post_select_offset : $orig_offset;
  1017. $limit = $post_select_limit ? $post_select_limit : 0;
  1018. $id_list = array();
  1019. $_total_count = 0;
  1020. while (!$result->EOF) {
  1021. $e = new Entry;
  1022. foreach($field_names as $key) {
  1023. $key = strtolower($key);
  1024. $e->$key = $result->fields($key);
  1025. }
  1026. $result->MoveNext();
  1027. if (empty($e)) break;
  1028. if (count($filters)) {
  1029. foreach ($filters as $f) {
  1030. if (!$f($e, $ctx)) {
  1031. continue 2;
  1032. }
  1033. }
  1034. }
  1035. Entry::load_meta($e);
  1036. $_total_count++;
  1037. if ( !is_null($total_count) ) {
  1038. if ( ($orig_limit > 0)
  1039. && ( ($_total_count-$offset) > $orig_limit) ) {
  1040. // collected all the entries; only count numbers;
  1041. continue;
  1042. }
  1043. }
  1044. if ($offset && ($j++ < $offset)) continue;
  1045. $e->entry_authored_on = $this->db2ts($e->entry_authored_on);
  1046. $e->entry_modified_on = $this->db2ts($e->entry_modified_on);
  1047. $id_list[] = $e->entry_id;
  1048. $entries[] = $e;
  1049. $this->_comment_count_cache[$e->entry_id] = $e->entry_comment_count;
  1050. $this->_ping_count_cache[$e->entry_id] = $e->entry_ping_count;
  1051. if ( is_null($total_count) ) {
  1052. // the request does not want total count; break early
  1053. if (($limit > 0) && (count($entries) >= $limit)) break;
  1054. }
  1055. }
  1056. if ( !is_null($total_count) )
  1057. $total_count = $_total_count;
  1058. if (!$no_resort) {
  1059. $sort_field = '';
  1060. if (isset($args['sort_by'])) {
  1061. if ($args['sort_by'] == 'title') {
  1062. $sort_field = 'entry_title';
  1063. } elseif ($args['sort_by'] == 'id') {
  1064. $sort_field = 'entry_id';
  1065. } elseif ($args['sort_by'] == 'status') {
  1066. $sort_field = 'entry_status';
  1067. } elseif ($args['sort_by'] == 'modified_on') {
  1068. $sort_field = 'entry_modified_on';
  1069. } elseif ($args['sort_by'] == 'author_id') {
  1070. $sort_field = 'entry_author_id';
  1071. } elseif ($args['sort_by'] == 'excerpt') {
  1072. $sort_field = 'entry_excerpt';
  1073. } elseif ($args['sort_by'] == 'comment_created_on') {
  1074. $sort_field = $args['sort_by'];
  1075. } elseif ($args['sort_by'] == 'score') {
  1076. $sort_field = $args['sort_by'];
  1077. } elseif ($args['sort_by'] == 'rate') {
  1078. $sort_field = $args['sort_by'];
  1079. } elseif ($args['sort_by'] == 'trackback_count') {
  1080. $sort_field = 'entry_ping_count';
  1081. } elseif (preg_match('/^field[:\.](.+)$/', $args['sort_by'], $match)) {
  1082. $sort_field = 'entry_field.' . $match[1];
  1083. } else {
  1084. $sort_field = 'entry_' . $args['sort_by'];
  1085. }
  1086. } else {
  1087. $sort_field = 'entry_authored_on';
  1088. }
  1089. if ($sort_field) {
  1090. if ($sort_field == 'score') {
  1091. $offset = $post_sort_offset ? $post_sort_offset : 0;
  1092. $limit = $post_sort_limit ? $post_sort_limit : 0;
  1093. $entries_tmp = array();
  1094. foreach ($entries as $e) {
  1095. $entries_tmp[$e->entry_id] = $e;
  1096. }
  1097. $scores = $this->fetch_sum_scores($args['namespace'], 'entry', $order,
  1098. $blog_filter . "\n" .
  1099. $entry_filter . "\n" .
  1100. $author_filter . "\n" .
  1101. $date_filter . "\n" .
  1102. $day_filter . "\n" .
  1103. $class_filter . "\n"
  1104. );
  1105. $entries_sorted = array();
  1106. foreach($scores as $score) {
  1107. if (--$offset >= 0) continue;
  1108. if (array_key_exists($score['objectscore_object_id'], $entries_tmp)) {
  1109. array_push($entries_sorted, $entries_tmp[$score['objectscore_object_id']]);
  1110. unset($entries_tmp[$score['objectscore_object_id']]);
  1111. if (--$limit == 0) break;
  1112. }
  1113. }
  1114. foreach ($entries_tmp as $et) {
  1115. if ($limit == 0) break;
  1116. if ($order == 'asc')
  1117. array_unshift($entries_sorted, $et);
  1118. else
  1119. array_push($entries_sorted, $et);
  1120. $limit--;
  1121. }
  1122. $entries = $entries_sorted;
  1123. } elseif ($sort_field == 'rate') {
  1124. $offset = $post_sort_offset ? $post_sort_offset : 0;
  1125. $limit = $post_sort_limit ? $post_sort_limit : 0;
  1126. $entries_tmp = array();
  1127. foreach ($entries as $e) {
  1128. $entries_tmp[$e->entry_id] = $e;
  1129. }
  1130. $scores = $this->fetch_avg_scores($args['namespace'], 'entry', $order,
  1131. $blog_filter . "\n" .
  1132. $entry_filter . "\n" .
  1133. $author_filter . "\n" .
  1134. $date_filter . "\n" .
  1135. $day_filter . "\n" .
  1136. $class_filter . "\n"
  1137. );
  1138. $entries_sorted = array();
  1139. foreach($scores as $score) {
  1140. if (--$offset >= 0) continue;
  1141. if (array_key_exists($score->objectscore_object_id, $entries_tmp)) {
  1142. array_push($entries_sorted, $entries_tmp[$score->objectscore_object_id]);
  1143. unset($entries_tmp[$score->objectscore_object_id]);
  1144. if (--$limit == 0) break;
  1145. }
  1146. }
  1147. foreach ($entries_tmp as $et) {
  1148. if ($limit == 0) break;
  1149. if ($order == 'asc')
  1150. array_unshift($entries_sorted, $et);
  1151. else
  1152. array_push($entries_sorted, $et);
  1153. $limit--;
  1154. }
  1155. $entries = $entries_sorted;
  1156. } else {
  1157. if (($sort_field == 'entry_status') || ($sort_field == 'entry_author_id') || ($sort_field == 'entry_id')
  1158. || ($sort_field == 'entry_comment_count') || ($sort_field == 'entry_ping_count')) {
  1159. $sort_fn = "if (\$a->$sort_field == \$b->$sort_field) return 0; return \$a->$sort_field < \$b->$sort_field ? -1 : 1;";
  1160. } else {
  1161. $sort_fn = "return strcmp(\$a->$sort_field,\$b->$sort_field);";
  1162. }
  1163. $sorter = create_function(
  1164. $order == 'asc' ? '$a,$b' : '$b,$a',

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