root/branches/release-39/php/lib/mtdb_base.php @ 2450

Revision 2450, 120.8 kB (checked in by takayama, 18 months ago)

Fixed BugId:79904
* Changed to use MTCommentLink
* Added support for filtering of no class to fetch_entries

  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
3# This program is distributed under the terms of the
4# GNU General Public License, version 2.
5#
6# $Id$
7
8class MTDatabaseBase extends ezsql {
9    var $savedqueries = array();
10    var $_entry_id_cache = array();
11    var $_author_id_cache = array();
12    var $_comment_count_cache = array();
13    var $_ping_count_cache = array();
14    var $_cat_id_cache = array();
15    var $_tag_id_cache = array();
16    var $_blog_id_cache = array();
17    var $_entry_link_cache = array();
18    var $_cat_link_cache = array();
19    var $_archive_link_cache = array();
20    var $_entry_tag_cache = array();
21    var $_blog_tag_cache = array();
22    var $_asset_tag_cache = array();
23    var $_blog_asset_tag_cache = array();
24    var $serializer;
25    var $id;
26
27    ## temporary until we class our objects properly
28    var $object_meta = array(
29        'blog' => array(
30            'commenter_authenticators:vchar',
31            'nofollow_urls:vinteger',
32            'follow_auth_links:vinteger',
33            'captcha_provider:vchar',
34            'template_set:vchar',
35            'page_layout:vchar',
36            'include_system:vinteger',
37            'include_cache:vinteger'),
38        'template' => array(
39            'page_layout:vchar',
40            'include_with_ssi:vinteger',
41            'cache_expire_type:vinteger',
42            'cache_expire_interval:vinteger',
43            'cache_expire_event:vchar'),
44        'asset' => array('image_width:vinteger',
45            'image_height:vinteger')
46    );
47    var $_meta_cache = array();
48
49    function MTDatabaseBase($dbuser, $dbpassword = '', $dbname = '',
50        $dbhost = '', $dbport = '', $dbsocket = '') {
51        $this->id = md5(uniqid('MTDatabaseBase',true));
52        $this->hide_errors();
53        $this->db($dbuser, $dbpassword, $dbname, $dbhost, $dbport, $dbsocket);
54    }
55
56    function unserialize($data) {
57        if (!$this->serializer) {
58            require_once("MTSerialize.php");
59            $this->serializer =& new MTSerialize();
60        }
61        return $this->serializer->unserialize($data);
62    }
63
64    function query($query) {
65        $this->savedqueries[] = $query;
66        parent::query($query);
67    }
68
69    function &resolve_url($path, $blog_id) {
70        $path = preg_replace('!/$!', '', $path);
71        $path = $this->escape($path);
72        $blog_id = intval($blog_id);
73        # resolve for $path -- one of:
74        #      /path/to/file.html
75        #      /path/to/index.html
76        #      /path/to/
77        #      /path/to
78        global $mt;
79        $index = $this->escape($mt->config('IndexBasename'));
80        $escindex = $this->escape($index);
81        foreach ( array($path, urldecode($path), urlencode($path)) as $p ) {
82            $sql = "
83                select *
84                  from mt_blog, mt_template, mt_fileinfo
85                  left outer join mt_templatemap on templatemap_id = fileinfo_templatemap_id
86                 where fileinfo_blog_id = $blog_id
87                       and ((fileinfo_url = '%1\$s' or fileinfo_url = '%1\$s/') or (fileinfo_url like '%1\$s/$escindex%%'))
88                   and blog_id = fileinfo_blog_id
89                   and template_id = fileinfo_template_id
90                 order by length(fileinfo_url) asc
91            ";
92            $rows = $this->get_results(sprintf($sql,$p), ARRAY_A);
93            if ($rows) {
94                break;
95            }
96        }
97        $path = $p;
98        if (!$rows) return null;
99
100        $found = false;
101        foreach ($rows as $row) {
102            $fiurl = $row['fileinfo_url'];
103            if ($fiurl == $path) {
104                $found = true;
105                break;
106            }
107            if ($fiurl == "$path/") {
108                $found = true;
109                break;
110            }
111            $ext = $row['blog_file_extension'];
112            if (!empty($ext)) $ext = '.' . $ext;
113            if ($fiurl == ($path.'/'.$index.$ext)) {
114                $found = true; break;
115            }
116            if ($found) break;
117        }
118        if (!$found) return null;
119        $data = array();
120        foreach ($row as $key => $value) {
121            if (preg_match('/^([a-z]+)/', $key, $matches)) {
122                $data[$matches[1]][$key] = $value;
123            }
124        }
125        $this->_blog_id_cache[$data['blog']['blog_id']] =& $data['blog'];
126        return $data;
127    }
128
129    function fetch_blogs($args) {
130        if ($blog_ids = $this->include_exclude_blogs($args)) {
131            $where = ' where blog_id ' . $blog_ids;
132        }
133        $sql = 'select * from mt_blog'
134            . $where . ' order by blog_name';
135        $res = $this->get_results($sql, ARRAY_A);
136        return $res;
137    }
138
139    function fetch_templates($args) {
140        if (isset($args['type'])) {
141            $type_filter = 'and template_type = \'' . $this->escape($args['type']) . '\'';
142        }
143        if (isset($args['blog_id'])) {
144            $blog_filter = 'and template_blog_id = ' . intval($args['blog_id']);
145        }
146        $sql = "select *
147                  from mt_template
148                 where 1 = 1
149                       $blog_filter
150                       $type_filter
151              order by template_name";
152        $result = $this->get_results($sql, ARRAY_A);
153        return $result;
154    }
155
156    function fetch_template_meta($type, $name, $blog_id, $global) {
157        if ($type === 'identifier') {
158            $col = 'template_identifier';
159            $type_filter = "";
160        } else {
161            $col = 'template_name';
162            $type_filter = "and template_type='$type'";
163        }
164        if (!isset($global)) {
165            $blog_filter = "and template_blog_id in (".$this->escape($blog_id).",0)";
166        } elseif ($global) {
167            $blog_filter = "and template_blog_id=0";
168        } else {
169            $blog_filter = "and template_blog_id=".$this->escape($blog_id);
170        }
171
172        $tmpl_name = $this->escape($name);
173
174        $sql = "
175            select
176                template_id, template_name, template_modified_on
177            from
178                mt_template
179            where
180                $col = '$tmpl_name'
181                $blog_filter
182                $type_filter
183            order by
184                template_blog_id desc";
185        $row = $this->get_row($sql, ARRAY_A);
186        if (!$row) return '';
187
188        $data = $this->get_meta('template', $row['template_id']);
189        return array_merge($row, $data);
190    }
191
192    function load_index_template(&$ctx, $tmpl, $blog_id = null) {
193        return $this->load_special_template($ctx, $tmpl, 'index');
194    }
195
196    function load_special_template(&$ctx, $tmpl, $type, $blog_id = null) {
197        $blog_id or $blog_id = $ctx->stash('blog_id');
198        $tmpl_name = $this->escape($tmpl);
199        $sql = "select * from mt_template" .
200            " where template_blog_id=$blog_id ".
201            ($tmpl ? " and (template_name='". $tmpl_name . "'" .
202            " or template_outfile='" . $tmpl_name ."'" .
203            " or template_identifier='" . $tmpl_name . "')" : "").
204            " and template_type='".$this->escape($type)."'";
205        list($row) = $this->get_results($sql, ARRAY_A);
206        if (!$row) return null;
207        return $row;
208    }
209
210    function fetch_config() {
211        $sql = "select * from mt_config";
212        list($row) = $this->get_results($sql, ARRAY_A);
213        if (!$row) return null;
214        return $row;
215    }
216
217    function category_link($cid, $args = null) {
218        if (isset($this->_cat_link_cache[$cid])) {
219            $url = $this->_cat_link_cache[$cid];
220        } else {
221            $sql = "select fileinfo_url, fileinfo_blog_id
222                      from mt_fileinfo, mt_templatemap
223                     where fileinfo_category_id = $cid
224                       and fileinfo_archive_type = 'Category'
225                       and templatemap_id = fileinfo_templatemap_id
226                       and templatemap_is_preferred = 1";
227            $rows = $this->get_results($sql, ARRAY_A);
228            if (count($rows)) {
229                $link =& $rows[0];
230            } else {
231                return null;
232            }
233            $blog =& $this->fetch_blog($link['fileinfo_blog_id']);
234            $blog_url = $blog['blog_site_url'];
235            $blog_url = preg_replace('!(https?://(?:[^/]+))/.*!', '$1', $blog_url);
236            $url = $blog_url . $link['fileinfo_url'];
237            $url = _strip_index($url, $blog);
238            $this->_cat_link_cache[$cid] = $url;
239        }
240        return $url;
241    }
242
243    function archive_link($ts, $at, $sql, $args) {
244        $blog_id = intval($args['blog_id']);
245        if (isset($this->_archive_link_cache[$blog_id.';'.$ts.';'.$at])) {
246            $url = $this->_archive_link_cache[$blog_id.';'.$ts.';'.$at];
247        } else {
248            if ($sql == '') {
249                $sql = "select fileinfo_url
250                          from mt_fileinfo, mt_templatemap
251                         where fileinfo_startdate = '$ts'
252                           and fileinfo_blog_id = $blog_id
253                           and fileinfo_archive_type = '".$this->escape($at)."'
254                           and templatemap_id = fileinfo_templatemap_id
255                           and templatemap_is_preferred = 1";
256            }
257            $rows = $this->get_results($sql, ARRAY_A);
258            if (count($rows)) {
259                $link =& $rows[0];
260            } else {
261                return null;
262            }
263            $blog =& $this->fetch_blog($blog_id);
264            $blog_url = $blog['blog_site_url'];
265            $blog_url = preg_replace('!(https?://(?:[^/]+))/.*!', '$1', $blog_url);
266            $url = $blog_url . $link['fileinfo_url'];
267            $url = _strip_index($url, $blog);
268            $this->_archive_link_cache[$ts.';'.$at] = $url;
269        }
270        return $url;
271    }
272
273    function entry_link($eid, $at = "Individual", $args = null) {
274        $blog_id = intval($args['blog_id']);
275        if (isset($this->_entry_link_cache[$eid.';'.$at])) {
276            $url = $this->_entry_link_cache[$eid.';'.$at];
277        } else {
278            if (preg_match('/Category/', $at)) {
279                $table = ", mt_placement";
280                $filter = "and placement_entry_id = $eid
281                           and fileinfo_category_id = placement_category_id
282                           and placement_is_primary = 1";
283            }
284            if (preg_match('/Page/', $at)) {
285                $entry = $this->fetch_page($eid);
286            } else {
287                $entry = $this->fetch_entry($eid);
288            }
289            $ts = $entry['entry_authored_on'];
290            if (preg_match('/Monthly$/', $at)) {
291                $ts = substr($ts, 0, 6) . '01000000';
292            } elseif (preg_match('/Daily$/', $at)) {
293                $ts = substr($ts, 0, 8) . '000000';
294            } elseif (preg_match('/Weekly$/', $at)) {
295                require_once("MTUtil.php");
296                list($ws, $we) = start_end_week($ts);
297                $ts = $ws;
298            } elseif (preg_match('/Yearly$/', $at)) {
299                $ts = substr($ts, 0, 4) . '0101000000';
300            } elseif ($at == 'Individual' || $at == 'Page') {
301                $filter .= "and fileinfo_entry_id = $eid";
302            }
303            if ($ts != $entry['entry_authored_on']) {
304                $filter .= " and fileinfo_startdate = '$ts'";
305            }
306            if (preg_match('/Author/', $at)) {
307                $filter .= " and fileinfo_author_id = ". $entry['entry_author_id'];
308            }
309            $sql = "select fileinfo_url, fileinfo_blog_id
310                     from mt_fileinfo, mt_templatemap $table
311                    where templatemap_id = fileinfo_templatemap_id
312                      and fileinfo_blog_id = $blog_id
313                      $filter
314                      and templatemap_archive_type = '$at'
315                      and templatemap_is_preferred = 1";
316            $rows = $this->get_results($sql, ARRAY_A);
317            if (count($rows)) {
318                $link =& $rows[0];
319            } else {
320                return null;
321            }
322            $blog =& $this->fetch_blog($link['fileinfo_blog_id']);
323            $blog_url = $blog['blog_site_url'];
324            $blog_url = preg_replace('!(https?://(?:[^/]+))/.*!', '$1', $blog_url);
325            $url = $blog_url . $link['fileinfo_url'];
326            $url = _strip_index($url, $blog);
327            $this->_entry_link_cache[$eid.';'.$at] = $url;
328        }
329        if ($at != 'Individual' && $at != 'Page') {
330            if (!$args || !isset($args['no_anchor'])) {
331                $url .= '#' . (!$args || isset($args['valid_html']) ? 'a' : '') .
332                        sprintf("%06d", $eid);
333            }
334        }
335        return $url;
336    }
337
338    /* recreation of generic load method functionality from MT::Object */
339    function &load($class, $terms = null, $args = null) {
340        $sql = "select * from mt_$class";
341        $where = '';
342        if ($terms) {
343            if (is_array($terms)) {
344                foreach ($terms as $col => $val) {
345                    $column = $class . '_' . $col;
346                    if (isset($args['range']) && isset($args['range'][$col])) {
347                        $range = '';
348                        if (isset($val[0]))
349                            $range = "$column > '" . $this->escape($val[0]) . "' ";
350                        if (isset($val[1])) {
351                            if ($range != '') $range .= ' and ';
352                            $range .= "$column < '" . $this->escape($val[0]) . "'";
353                        }
354                        $where .= " and ($range)";
355                    } elseif (isset($args['range_incl']) && isset($args['range_incl'][$col])) {
356                        $range = '';
357                        if (isset($val[0]))
358                            $range = "$column >= '" . $this->escape($val[0]) . "' ";
359                        if (isset($val[1])) {
360                            if ($range != '') $range .= ' and ';
361                            $range .= "$column <= '" . $this->escape($val[0]) . "'";
362                        }
363                        $where .= " and ($range)";
364                    } else {
365                        if (is_array($val)) {
366                            $list = '';
367                            foreach ($val as $item) {
368                                if ($list != '') $list .= ',';
369                                $list .= "'" . $this->escape($item). "'";
370                            }
371                            $where .= " and ($column in ($list))";
372                        } else {
373                            $where .= " and ($column='".$this->escape($val)."')";
374                        }
375                    }
376                }
377                $where = preg_replace('/^ and/', '', $where);
378            } else {
379                $where = " ($class"."_id=".intval($terms).")";
380            }
381            $sql .= ' where ' . $where;
382        }
383        if (isset($args['sort']) or isset($args['direction'])) {
384            $order = $args['sort'];
385            $order or $order = 'id';
386            $dir = $args['direction'] == 'descend' ? 'desc' : 'asc';
387            $sql .= " order by " . $class . "_" . $order . " $dir";
388        }
389        if (isset($args['limit']) or isset($args['offset'])) {
390            $sql .= ' <LIMIT>';
391            $sql = $this->apply_limit_sql($sql, $args['limit'], $args['offset']);
392        }
393        return $this->get_results($sql, ARRAY_A);
394    }
395
396    function get_template_text($ctx, $module, $blog_id = null, $type = 'custom', $global = null) {
397        $blog_id or $blog_id = $ctx->stash('blog_id');
398        if ($type === 'custom' || $type === 'widget'|| $type === 'widgetset') {
399            $col = 'template_name';
400            $type_filter = "and template_type='$type'";
401        } else {
402            $col = 'template_identifier';
403            $type_filter = "";
404        }
405        if (!isset($global)) {
406            $blog_filter = "template_blog_id in (".$this->escape($blog_id).",0)";
407        } elseif ($global) {
408            $blog_filter = "template_blog_id=0";
409        } else {
410            $blog_filter = "template_blog_id=".$this->escape($blog_id);
411        }
412        $row = $this->get_row("
413            select template_text, template_modified_on, template_linked_file, template_linked_file_mtime, template_linked_file_size
414              from mt_template
415             where $blog_filter
416               and $col='".$this->escape($module)."'
417               $type_filter
418               order by template_blog_id desc", ARRAY_N);
419        if (!$row) return '';
420        list($tmpl, $ts, $file, $mtime, $size) = $row;
421        if ($file) {
422            if (!file_exists($file)) {
423                $blog = $ctx->stash('blog');
424                if ($blog['blog_id'] != $blog_id) {
425                    $blog = $this->fetch_blog($blog_id);
426                }
427                $path = $blog['blog_site_path'];
428                if (!preg_match('![\\/]$!', $path))
429                    $path .= '/';
430                $path .= $file;
431                if (is_file($path) && is_readable($path))
432                    $file = $path;
433                else
434                    $file = '';
435            }
436            if ($file) {
437                if ((filemtime($file) > $mtime) || (filesize($file) != $size)) {
438                    $contents = @file($file);
439                    $tmpl = implode('', $contents);
440                }
441            }
442        }
443        return $tmpl;
444    }
445
446    function &fetch_pages($args) {
447        $args['class'] = 'page';
448        return $this->fetch_entries($args);
449    }
450
451    function &fetch_entries($args, $total_count = NULL) {
452        if ($sql = $this->include_exclude_blogs($args)) {
453            $blog_filter = 'and entry_blog_id ' . $sql;
454        } elseif (isset($args['blog_id'])) {
455            $blog_id = intval($args['blog_id']);
456            $blog_filter = 'and entry_blog_id = ' . $blog_id;
457            $blog = $this->fetch_blog($blog_id);
458        }
459
460        // determine any custom fields that we should filter on
461        $fields = array();
462        foreach ($args as $name => $v)
463            if (preg_match('/^field___(\w+)$/', $name, $m))
464                $fields[$m[1]] = $v;
465
466        # automatically include offset if in request
467        if ($args['offset'] == 'auto') {
468            $args['offset'] = 0;
469            if ($args['limit'] || $args['lastn']) {
470                if ($_REQUEST['offset'] > 0) {
471                    $args['offset'] = $_REQUEST['offset'];
472                }
473            }
474        }
475
476        if ($args['limit'] > 0) {
477            $args['lastn'] = $args['limit'];
478        } elseif (!isset($args['days']) && !isset($args['lastn'])) {
479            if ($days = $blog['blog_days_on_index']) {
480                if (!isset($args['recently_commented_on'])) {
481                    $args['days'] = $days;
482                }
483            } elseif ($posts = $blog['blog_entries_on_index']) {
484                $args['lastn'] = $posts;
485            }
486        }
487        if ($args['limit'] == 'auto') {
488            if (($_REQUEST['limit'] > 0) && ($_REQUEST['limit'] < $args['lastn'])) {
489                $args['lastn'] = intval($_REQUEST['limit']);
490            }
491        }
492
493        if (isset($args['include_blogs']) or isset($args['exclude_blogs'])) {
494            $blog_ctx_arg = isset($args['include_blogs']) ?
495                array('include_blogs' => $args['include_blogs']) :
496                array('exclude_blogs' => $args['exclude_blogs']);
497        }
498
499        # a context hash for filter routines
500        $ctx = array();
501        $filters = array();
502
503        if (!isset($_REQUEST['entry_ids_published'])) {
504            $_REQUEST['entry_ids_published'] = array();
505        }
506
507        if (isset($args['unique'])) {
508            $filters[] = create_function('$e,$ctx', 'return !isset($ctx["entry_ids_published"][$e["entry_id"]]);');
509            $ctx['entry_ids_published'] = &$_REQUEST['entry_ids_published'];
510        }
511
512        # special case for selecting a particular entry
513        if (isset($args['entry_id'])) {
514            $entry_filter = 'and entry_id = '.$args['entry_id'];
515            $start = ''; $end = ''; $limit = 1; $blog_filter = ''; $day_filter = '';
516        } else {
517            $entry_filter = '';
518        }
519
520        # special case for excluding a particular entry
521        if (isset($args['not_entry_id'])) {
522            $entry_filter .= ' and entry_id != '.$args['not_entry_id'];
523        }
524
525        $entry_list = array();
526
527        # Adds a category filter to the filters list.
528        $cat_class = 'category';
529        if (!isset($args['class']))
530            $args['class'] = 'entry';
531        if ($args['class'] == 'page')
532            $cat_class = 'folder';
533
534        if (isset($args['category']) or isset($args['categories'])) {
535            $category_arg = isset($args['category']) ? $args['category'] : $args['categories'];
536            require_once("MTUtil.php");
537            if (!preg_match('/\b(AND|OR|NOT)\b|\(|\)/i', $category_arg)) {
538                $not_clause = false;
539                $cats = cat_path_to_category($category_arg, $blog_ctx_arg, $cat_class);
540                if (count($cats)) {
541                    $category_arg = '';
542                    foreach ($cats as $cat) {
543                        if ($category_arg != '')
544                            $category_arg .= '|| ';
545                        $category_arg .= '#' . $cat['category_id'];
546                    }
547                    $category_arg = '(' . $category_arg . ')';
548                }
549            } else {
550                $not_clause = preg_match('/\bNOT\b/i', $category_arg);
551                if ($blog_ctx_arg)
552                    $cats =& $this->fetch_categories(array_merge($blog_ctx_arg, array('show_empty' => 1, 'class' => $cat_class)));
553                else
554                    $cats =& $this->fetch_categories(array('blog_id' => $blog_id, 'show_empty' => 1, 'class' => $cat_class));
555            }
556            if (!is_array($cats)) $cats = array();
557            $cexpr = create_cat_expr_function($category_arg, $cats, array('children' => $args['include_subcategories']));
558            if ($cexpr) {
559                $cmap = array();
560                $cat_list = array();
561                foreach ($cats as $cat)
562                    $cat_list[] = $cat['category_id'];
563                $pl =& $this->fetch_placements(array('category_id' => $cat_list));
564                if ($pl) {
565                    foreach ($pl as $p) {
566                        $cmap[$p['placement_entry_id']][$p['placement_category_id']]++;
567                        if (!$not_clause)
568                            $entry_list[$p['placement_entry_id']] = 1;
569                    }
570                }
571                $ctx['p'] =& $cmap;
572                $filters[] = $cexpr;
573            } else {
574                return null;
575            }
576        } elseif (isset($args['category_id'])) {
577            require_once("MTUtil.php");
578            $cat = $this->fetch_category($args['category_id']);
579            if ($cat) {
580                $cats = array($cat);
581                $cmap = array();
582                $cexpr = create_cat_expr_function($cat['category_label'], $cats, array('children' => $args['include_subcategories']));
583                $pl =& $this->fetch_placements(array('category_id' => array($cat['category_id'])));
584                if ($pl) {
585                    foreach ($pl as $p) {
586                        $cmap[$p['placement_entry_id']][$p['placement_category_id']]++;
587                    }
588                    $ctx['p'] =& $cmap;
589                    $filters[] = $cexpr;
590                } else {
591                    # this category have no entries (or pages)
592                    return null;
593                }
594            }
595        }
596        if ((0 == count($filters)) && (isset($args['show_empty']) && (1 == $args['show_empty']))) {
597            return null;
598        }
599
600        # Adds a tag filter to the filters list.
601        if (isset($args['tags']) or isset($args['tag'])) {
602            $tag_arg = isset($args['tag']) ? $args['tag'] : $args['tags'];
603            require_once("MTUtil.php");
604            $not_clause = preg_match('/\bNOT\b/i', $tag_arg);
605
606            require_once("MTUtil.php");
607            $include_private = 0;
608            $tag_array = tag_split($tag_arg);
609            foreach ($tag_array as $tag) {
610                if ($tag && (substr($tag,0,1) == '@')) {
611                    $include_private = 1;
612                }
613            }
614            if (isset($blog_ctx_arg))
615                $tags =& $this->fetch_entry_tags(array($blog_ctx_arg, 'tag' => $tag_arg, 'include_private' => $include_private, 'class' => $args['class']));
616            else
617                $tags =& $this->fetch_entry_tags(array('blog_id' => $blog_id, 'tag' => $tag_arg, 'include_private' => $include_private, 'class' => $args['class']));
618            if (!is_array($tags)) $tags = array();
619            $cexpr = create_tag_expr_function($tag_arg, $tags);
620
621            if ($cexpr) {
622                $tmap = array();
623                $tag_list = array();
624                foreach ($tags as $tag) {
625                    $tag_list[] = $tag['tag_id'];
626                }
627                if (isset($blog_ctx_arg))
628                    $ot =& $this->fetch_objecttags(array('tag_id' => $tag_list, 'datasource' => 'entry', $blog_ctx_arg));
629                elseif ($args['blog_id'])
630                    $ot =& $this->fetch_objecttags(array('tag_id' => $tag_list, 'datasource' => 'entry', 'blog_id' => $args['blog_id']));
631                if ($ot) {
632                    foreach ($ot as $o) {
633                            $tmap[$o['objecttag_object_id']][$o['objecttag_tag_id']]++;
634                        if (!$not_clause)
635                            $entry_list[$o['objecttag_object_id']] = 1;
636                    }
637                }
638                $ctx['t'] =& $tmap;
639                $filters[] = $cexpr;
640            } else {
641                return null;
642            }
643        }
644
645        # Adds a score or rate filter to the filters list.
646        if (isset($args['namespace'])) {
647            require_once("MTUtil.php");
648            $arg_names = array('min_score', 'max_score', 'min_rate', 'max_rate', 'min_count', 'max_count' );
649            foreach ($arg_names as $n) {
650                if (isset($args[$n])) {
651                    $rating_args = $args[$n];
652                    $cexpr = create_rating_expr_function($rating_args, $n, $args['namespace']);
653                    if ($cexpr) {
654                        $filters[] = $cexpr;
655                    } else {
656                        return null;
657                    }
658                }
659            }
660
661            if (isset($args['scored_by'])) {
662                $voter = $this->fetch_author_by_name($args['scored_by']);
663                if (!$voter) {
664                    echo "Invalid scored by filter: ".$args['scored_by'];
665                    return null;
666                }
667                $cexpr = create_rating_expr_function($voter['author_id'], 'scored_by', $args['namespace']);
668                if ($cexpr) {
669                    $filters[] = $cexpr;
670                } else {
671                    return null;
672                }
673            }
674        }
675
676        if (count($entry_list) && ($entry_filter == '')) {
677            $entry_list = implode(",", array_keys($entry_list));
678            # set a reasonable cap on the entry list cache. if
679            # user is selecting something too big, then they'll
680            # just have to wait through a scan.
681            if (strlen($entry_list) < 2048)
682                $entry_filter = "and entry_id in ($entry_list)";
683        }
684
685        if (isset($args['author']))
686            $author_filter = 'and author_name = \'' .
687                $this->escape($args['author']) . "'";
688
689        $start = isset($args['current_timestamp'])
690            ? $args['current_timestamp'] : null;
691        $end = isset($args['current_timestamp_end'])
692            ? $args['current_timestamp_end'] : null;
693        if ($start and $end) {
694            $start = $this->ts2db($start);
695            $end = $this->ts2db($end);
696            $date_filter = "and entry_authored_on between '$start' and '$end'";
697        } elseif ($start) {
698            $start = $this->ts2db($start);
699            $date_filter = "and entry_authored_on >= '$start'";
700        } elseif ($end) {
701            $end = $this->ts2db($end);
702            $date_filter = "and entry_authored_on <= '$end'";
703        } else {
704            $date_filter = '';
705        }
706
707        if (isset($args['lastn'])) {
708            if (!isset($args['entry_id'])) $limit = $args['lastn'];
709        } elseif (isset($args['days']) && !$date_filter) {
710            $day_filter = 'and ' . $this->limit_by_day_sql('entry_authored_on', intval($args['days']));
711        } else {
712            if ((!isset($args['current_timestamp']) &&
713                !isset($args['current_timestamp_end'])) &&
714                ($limit <= 0) &&
715                (!isset($args['category'])) &&
716                (!isset($args['categories'])) &&
717                (!isset($args['category_id'])) &&
718                (isset($blog))) {
719                if ($days = $blog['blog_days_on_index']) {
720                    if (!isset($args['recently_commented_on'])) {
721                        $day_filter = 'and ' . $this->limit_by_day_sql('entry_authored_on', $days);
722                    }
723                } elseif ($posts = $blog['blog_entries_on_index']) {
724                    $limit = $posts;
725                }
726            }
727        }
728
729        if (isset($args['sort_order'])) {
730            if ($args['sort_order'] == 'ascend') {
731                $order = 'asc';
732            } else if ($args['sort_order'] == 'descend') {
733                $order = 'desc';
734            }
735        } 
736        if (!isset($order)) {
737            $order = 'desc';
738            if (isset($blog) && isset($blog['blog_sort_order_posts'])) {
739                if ($blog['blog_sort_order_posts'] == 'ascend') {
740                    $order = 'asc';
741                }
742            }
743        }
744
745        if (isset($args['class'])) {
746            $class = $this->escape($args['class']);
747        } else {
748            $class = 'entry';
749        }
750        $class_filter = "and entry_class='$class'";
751        if ($args['class'] == '*') $class_filter = '';
752       
753        $join_score = "";
754        $distinct = "";
755        if ( isset($args['sort_by'])
756          && (($args['sort_by'] == 'score') || ($args['sort_by'] == 'rate')) ) {
757            $join_score = "left join mt_objectscore on objectscore_object_id = entry_id and objectscore_namespace='"
758                . $args['namespace']."' and objectscore_object_ds='".$class."'";
759            $distinct = " distinct";
760        }
761
762        if (isset($args['offset']))
763            $offset = $args['offset'];
764
765        if (isset($args['limit'])) {
766            if (isset($args['sort_by'])) { 
767                if ($args['sort_by'] == 'title') { 
768                    $sort_field = 'entry_title'; 
769                } elseif ($args['sort_by'] == 'id') { 
770                    $sort_field = 'entry_id'; 
771                } elseif ($args['sort_by'] == 'status') { 
772                    $sort_field = 'entry_status'; 
773                } elseif ($args['sort_by'] == 'modified_on') { 
774                    $sort_field = 'entry_modified_on'; 
775                } elseif ($args['sort_by'] == 'author_id') { 
776                    $sort_field = 'entry_author_id'; 
777                } elseif ($args['sort_by'] == 'excerpt') { 
778                    $sort_field = 'entry_excerpt'; 
779                } elseif ($args['sort_by'] == 'comment_created_on') { 
780                    $sort_field = $args['sort_by']; 
781                } elseif ($args['sort_by'] == 'score' || $args['sort_by'] == 'rate') { 
782                    $post_sort_limit = $limit;
783                    $post_sort_offset = $offset;
784                    $limit = 0; $offset = 0;
785                } elseif ($args['sort_by'] == 'trackback_count') {
786                    $sort_field = 'entry_ping_count'; 
787                } elseif (preg_match('/field[:\.]/', $args['sort_by'])) {
788                    $no_resort = 0;
789                } else { 
790                    $sort_field = 'entry_' . $args['sort_by']; 
791                } 
792                if ($sort_field) $no_resort = 1; 
793            } 
794            else {
795                $sort_field ='entry_authored_on';
796            }
797            if ($sort_field) {
798                $base_order = ($args['sort_order'] == 'ascend' ? 'asc' : 'desc');
799                $base_order or $base_order = 'desc';
800            }
801        } else {
802            $base_order = 'desc';
803            if (isset($args['base_sort_order'])) {
804                if ($args['base_sort_order'] == 'ascend')
805                    $base_order = 'asc';
806            }
807            $sort_field ='entry_authored_on'; 
808            $no_resort = 0;
809        }
810
811        if (count($filters)) {
812            $post_select_limit = $limit;
813            $post_select_offset = $offset;
814            $limit = 0; $offset = 0;
815        }
816
817        if (count($fields)) {
818            $meta_join_num = 1;
819            $meta_join = "";
820            $entry_meta = array();
821            foreach ($this->object_meta['entry'] as $meta) {
822                list($meta_key, $meta_type) = preg_split('/:/', $meta);
823                $entry_meta[$meta_key] = $meta_type;
824            }
825            foreach ($fields as $name => $value) {
826                $meta_col = $entry_meta['field.' . $name];
827                if (!$meta_col) return null; # invalid column; can't select
828
829                $value = $this->escape($value);
830                $meta_join .= " join mt_entry_meta entry_meta$meta_join_num on (entry_meta$meta_join_num.entry_meta_entry_id = entry_id
831                and entry_meta$meta_join_num.entry_meta_type = 'field.$name'
832                and entry_meta$meta_join_num.entry_meta_$meta_col='$value')\n";
833                $meta_join_num++;
834            }
835        }
836
837        $sql = "
838            select$distinct mt_entry.*, mt_placement.*, mt_author.*,
839                   mt_trackback.*
840              from mt_entry
841              left outer join mt_trackback on trackback_entry_id = entry_id
842              $meta_join
843              $join_score
844              left outer join mt_placement on (placement_entry_id = entry_id
845                and placement_is_primary = 1),
846                   mt_author
847             where entry_status = 2
848               and entry_author_id = author_id
849                   $blog_filter
850                   $entry_filter
851                   $author_filter
852                   $date_filter
853                   $day_filter
854                   $class_filter
855        ";
856
857        if ($sort_field) {
858            $sql .= "
859                order by $sort_field $base_order";
860        }
861        if (isset($args['recently_commented_on'])) {
862            $rco = $args['recently_commented_on'];
863            $sql = $this->entries_recently_commented_on_sql($sql);
864            $sql = $this->apply_limit_sql($sql, count($filters) ? null : $rco);
865            $args['sort_by'] or $args['sort_by'] = 'comment_created_on';
866            $args['sort_order'] or $args['sort_order'] = 'descend';
867            $post_select_limit = $rco;
868            $no_resort = 1;
869        } elseif ( !is_null($total_count) ) {
870            $orig_limit = $limit;
871            $orig_offset = $offset;
872        } else {
873            $sql = $this->apply_limit_sql($sql . " <LIMIT>", $limit, $offset);
874        }
875
876        $result = $this->query_start($sql);
877        if (!$result) return null;
878
879        $entries = array();
880        $j = 0;
881        $offset = $post_select_offset ? $post_select_offset : $orig_offset;
882        $limit = $post_select_limit ? $post_select_limit : 0;
883        $id_list = array();
884        $_total_count = 0;
885        while (true) {
886            $e = $this->query_fetch(ARRAY_A);
887            if (!isset($e)) break;
888            if (count($filters)) {
889                foreach ($filters as $f) {
890                    $old_result = $this->result;
891                    if (!$f($e, $ctx)) {
892                        $this->result = $old_result;
893                        continue 2;
894                    }
895                    $this->result = $old_result;
896                }
897            }
898            $_total_count++;
899            if ( !is_null($total_count) ) {
900                if ( ($orig_limit > 0)
901                  && ( ($_total_count-$offset) > $orig_limit) ) {
902                    // collected all the entries; only count numbers;
903                    continue;
904                }
905            }
906            if ($offset && ($j++ < $offset)) continue;
907            $e['entry_authored_on'] = $this->db2ts($e['entry_authored_on']);
908            $e['entry_modified_on'] = $this->db2ts($e['entry_modified_on']);
909            $e = $this->expand_meta($e);
910            $id_list[] = $e['entry_id'];
911            $entries[] = $e;
912            $this->_comment_count_cache[$e['entry_id']] = $e['entry_comment_count'];
913            $this->_ping_count_cache[$e['entry_id']] = $e['entry_ping_count'];
914            if ( is_null($total_count) ) {
915                // the request does not want total count; break early
916                if (($limit > 0) && (count($entries) >= $limit)) break;
917            }
918        }
919        if ( !is_null($total_count) )
920            $total_count = $_total_count;
921
922        if (!$no_resort) {
923            $sort_field = '';
924            if (isset($args['sort_by'])) {
925                if ($args['sort_by'] == 'title') {
926                    $sort_field = 'entry_title';
927                } elseif ($args['sort_by'] == 'id') {
928                    $sort_field = 'entry_id';
929                } elseif ($args['sort_by'] == 'status') {
930                    $sort_field = 'entry_status';
931                } elseif ($args['sort_by'] == 'modified_on') {
932                    $sort_field = 'entry_modified_on';
933                } elseif ($args['sort_by'] == 'author_id') {
934                    $sort_field = 'entry_author_id';
935                } elseif ($args['sort_by'] == 'excerpt') {
936                    $sort_field = 'entry_excerpt';
937                } elseif ($args['sort_by'] == 'comment_created_on') {
938                    $sort_field = $args['sort_by'];
939                } elseif ($args['sort_by'] == 'score') {
940                    $sort_field = $args['sort_by'];
941                } elseif ($args['sort_by'] == 'rate') {
942                    $sort_field = $args['sort_by'];
943                } elseif ($args['sort_by'] == 'trackback_count') {
944                    $sort_field = 'entry_ping_count'; 
945                } elseif (preg_match('/^field[:\.](.+)$/', $args['sort_by'], $match)) {
946                    $sort_field = 'entry_field.' . $match[1];
947                } else {
948                    $sort_field = 'entry_' . $args['sort_by'];
949                }
950            } else {
951                $sort_field = 'entry_authored_on';
952            }
953            if ($sort_field) {
954                if ($sort_field == 'score') {
955                    $offset = $post_sort_offset ? $post_sort_offset : 0;
956                    $limit = $post_sort_limit ? $post_sort_limit : 0;
957                    $entries_tmp = array();
958                    foreach ($entries as $e) {
959                        $entries_tmp[$e['entry_id']] = $e;
960                    }
961                    $scores = $this->fetch_sum_scores($args['namespace'], 'entry', $order,
962                        $blog_filter . "\n" .
963                        $entry_filter . "\n" .
964                        $author_filter . "\n" .
965                        $date_filter . "\n" .
966                        $day_filter . "\n" .
967                        $class_filter . "\n"
968                    );
969                    $entries_sorted = array();
970                    foreach($scores as $score) {
971                        if (--$offset >= 0) continue;
972                        if (array_key_exists($score['objectscore_object_id'], $entries_tmp)) {
973                            array_push($entries_sorted, $entries_tmp[$score['objectscore_object_id']]);
974                            unset($entries_tmp[$score['objectscore_object_id']]);
975                            if (--$limit == 0) break;
976                        }
977                    }
978                    foreach ($entries_tmp as $et) {
979                        if ($limit == 0) break;
980                        if ($order == 'asc')
981                            array_unshift($entries_sorted, $et);
982                        else
983                            array_push($entries_sorted, $et);
984                        $limit--;
985                    }
986                    $entries = $entries_sorted;
987                } elseif ($sort_field == 'rate') {
988                    $offset = $post_sort_offset ? $post_sort_offset : 0;
989                    $limit = $post_sort_limit ? $post_sort_limit : 0;
990                    $entries_tmp = array();
991                    foreach ($entries as $e) {
992                        $entries_tmp[$e['entry_id']] = $e;
993                    }
994                    $scores = $this->fetch_avg_scores($args['namespace'], 'entry', $order,
995                        $blog_filter . "\n" .
996                        $entry_filter . "\n" .
997                        $author_filter . "\n" .
998                        $date_filter . "\n" .
999                        $day_filter . "\n" .
1000                        $class_filter . "\n"
1001                    );
1002                    $entries_sorted = array();
1003                    foreach($scores as $score) {
1004                        if (--$offset >= 0) continue;
1005                        if (array_key_exists($score['objectscore_object_id'], $entries_tmp)) {
1006                            array_push($entries_sorted, $entries_tmp[$score['objectscore_object_id']]);
1007                            unset($entries_tmp[$score['objectscore_object_id']]);
1008                            if (--$limit == 0) break;
1009                        }
1010                    }
1011                    foreach ($entries_tmp as $et) {
1012                        if ($limit == 0) break;
1013                        if ($order == 'asc')
1014                            array_unshift($entries_sorted, $et);
1015                        else
1016                            array_push($entries_sorted, $et);
1017                        $limit--;
1018                    }
1019                    $entries = $entries_sorted;
1020                } else {
1021                    if (($sort_field == 'entry_status') || ($sort_field == 'entry_author_id') || ($sort_field == 'entry_id')
1022                          || ($sort_field == 'entry_comment_count') || ($sort_field == 'entry_ping_count')) {
1023                        $sort_fn = "if (\$a['$sort_field'] == \$b['$sort_field']) return 0; return \$a['$sort_field'] < \$b['$sort_field'] ? -1 : 1;";
1024                    } else {
1025                        $sort_fn = "return strcmp(\$a['$sort_field'],\$b['$sort_field']);";
1026                    }
1027                    $sorter = create_function(
1028                        $order == 'asc' ? '$a,$b' : '$b,$a',
1029                        $sort_fn);
1030                    usort($entries, $sorter);
1031                }
1032            }
1033        }
1034
1035        if (count($id_list) <= 30) { # TODO: find a good upper limit
1036            # pre-cache comment counts and categories for these entries
1037            $this->cache_categories($id_list);
1038            $this->cache_permalinks($id_list);
1039        }
1040
1041        return $entries;
1042    }
1043
1044    function fetch_plugin_config($plugin, $scope = "system") {
1045        if ($scope != 'system') {
1046            $key = 'configuration:'.$scope;
1047        } else {
1048            $key = 'configuration';
1049        }
1050        return $this->fetch_plugin_data($plugin, $key);
1051    }
1052
1053    function fetch_plugin_data($plugin, $key) {
1054        $plugin = $this->escape($plugin);
1055        $key = $this->escape($key);
1056        $sql = "
1057            select plugindata_data from mt_plugindata
1058             where plugindata_plugin = '$plugin'
1059               and plugindata_key = '$key'";
1060        $data = $this->get_var($sql);
1061        if ($data) {
1062            return $this->unserialize($data);
1063        }
1064        return null;
1065    }
1066
1067    function &fetch_entry_tags($args) {
1068        $class = 'entry';
1069        if (isset($args['class'])) {
1070            $class = $args['class'];
1071        }
1072
1073        # load tags
1074        if (isset($args['entry_id'])) {
1075            if (!isset($args['tags']) && !isset($args['tag'])) {
1076                if (isset($this->_entry_tag_cache[$args['entry_id']])) {
1077                    return $this->_entry_tag_cache[$args['entry_id']];
1078                }
1079            }
1080            $entry_filter = 'and objecttag_tag_id in (select objecttag_tag_id from mt_objecttag where objecttag_object_id='.intval($args['entry_id']).')';
1081        }
1082
1083        $blog_filter = $this->include_exclude_blogs($args);
1084        if ($blog_filter == '' and isset($args['blog_id'])) {
1085            if (!isset($args['tags']) && !isset($args['tag'])) {
1086                if (!isset($args['entry_id'])) {
1087                    if (isset($this->_blog_tag_cache[$args['blog_id'].":$class"])) {
1088                        return $this->_blog_tag_cache[$args['blog_id'].":$class"];
1089                    }
1090                }
1091            }
1092            $blog_filter = ' = '. intval($args['blog_id']);
1093        }
1094        if ($blog_filter != '') 
1095            $blog_filter = 'and objecttag_blog_id ' . $blog_filter;
1096
1097        if (!isset($args['include_private'])) {
1098            $private_filter = 'and (tag_is_private = 0 or tag_is_private is null)';
1099        }
1100        if (isset($args['tags']) && ($args['tags'] != '')) {
1101            $tag_list = '';
1102            require_once("MTUtil.php");
1103            $tag_array = tag_split($args['tags']);
1104            foreach ($tag_array as $tag) {
1105                if ($tag_list != '') $tag_list .= ',';
1106                $tag_list .= "'" . $this->escape($tag) . "'";
1107            }
1108            if ($tag_list != '') {
1109                $tag_filter = 'and (tag_name in (' . $tag_list . '))';
1110                $private_filter = '';
1111            }
1112        }
1113
1114        $sort_col = isset($args['sort_by']) ? $args['sort_by'] : 'name';
1115        $sort_col = "tag_$sort_col";
1116        if (isset($args['sort_order']) and $args['sort_order'] == 'descend') {
1117            $order = 'desc';
1118        } else {
1119            $order = 'asc';
1120        }
1121        $id_order = '';
1122        if ($sort_col == 'tag_name') {
1123            $sort_col = 'lower(tag_name)';
1124        }else{
1125            $id_order = ', lower(tag_name)';
1126        }
1127
1128        $sql = "
1129            select tag_id, tag_name, count(*) as tag_count
1130             from mt_tag, mt_objecttag, mt_entry
1131             where objecttag_tag_id = tag_id
1132               and entry_id = objecttag_object_id and objecttag_object_datasource='entry'
1133               and entry_status = 2
1134                   and entry_class = '$class'
1135                   $blog_filter
1136                   $tag_filter
1137                   $entry_filter
1138                   $private_filter
1139            group by tag_id, tag_name
1140            order by $sort_col $order $id_order";
1141        $tags = $this->get_results($sql, ARRAY_A);
1142        if (!isset($args['tag'])) {
1143            if ($args['entry_id'])
1144                $this->_entry_tag_cache[$args['entry_id']] = $tags;
1145            elseif ($args['blog_id'])
1146                $this->_blog_tag_cache[$args['blog_id'].":$class"] = $tags;
1147        }
1148        return $tags;
1149    }
1150
1151    function &fetch_asset_tags($args) {
1152
1153        # load tags by asset
1154        if (!isset($args['include_private'])) {
1155            $private_filter = 'and (tag_is_private = 0 or tag_is_private is null)';
1156        }
1157
1158        if (isset($args['asset_id'])) {
1159            if (isset($args['tags'])) {
1160                if (isset($this->_asset_tag_cache[$args['asset_id']]))
1161                    return $this->_asset_tag_cache[$args['asset_id']];
1162            }
1163            $asset_filter = 'and objecttag_object_id = '.intval($args['asset_id']);
1164        }
1165       
1166        if (isset($args['blog_id'])) {
1167            if (!isset($args['tags'])) {
1168                if (isset($this->_blog_asset_tag_cache[$args['blog_id']]))
1169                    return $this->_blog_asset_tag_cache[$args['blog_id']];
1170            }
1171            $blog_filter = 'and objecttag_blog_id = '.intval($args['blog_id']);
1172        }
1173
1174        if (isset($args['tags']) && ($args['tags'] != '')) {
1175            $tag_list = '';
1176            require_once("MTUtil.php");
1177            $tag_array = tag_split($args['tags']);
1178            foreach ($tag_array as $tag) {
1179                if ($tag_list != '') $tag_list .= ',';
1180                $tag_list .= "'" . $this->escape($tag) . "'";
1181            }
1182            if ($tag_list != '') {
1183                $tag_filter = 'and (tag_name in (' . $tag_list . '))';
1184                $private_filter = '';
1185            }
1186        }
1187
1188        $sort_col = isset($args['sort_by']) ? $args['sort_by'] : 'name';
1189        $sort_col = "tag_$sort_col";
1190        if (isset($args['sort_order']) and $args['sort_order'] == 'descend')
1191            $order = 'desc';
1192        else
1193            $order = 'asc';
1194
1195        $id_order = '';
1196        if ($sort_col == 'tag_name')
1197            $sort_col = 'lower(tag_name)';
1198        else
1199            $id_order = ', lower(tag_name)';
1200
1201        $sql = "
1202            select tag_id, tag_name, count(*) as tag_count
1203            from mt_tag, mt_objecttag, mt_asset
1204            where objecttag_tag_id = tag_id
1205                and asset_id = objecttag_object_id and objecttag_object_datasource='asset'
1206                $blog_filter
1207                $private_filter
1208                $tag_filter
1209                $asset_filter
1210            group by tag_id, tag_name
1211            order by $sort_col $order $id_order
1212        ";
1213        $tags = $this->get_results($sql, ARRAY_A);
1214        if (isset($args['tags'])) {
1215            if ($args['asset_id'])
1216                $this->_asset_tag_cache[$args['asset_id']] = $tags;
1217            elseif ($args['blog_id'])
1218                $this->_blog_asset_tag_cache[$args['blog_id']] = $tags;
1219        }
1220        return $tags;
1221    }
1222
1223    function &fetch_folders($args) {
1224        $args['class'] = 'folder';
1225        return $this->fetch_categories($args);
1226    }
1227
1228    function &fetch_categories($args) {
1229        # load categories
1230
1231        if ($blog_filter = $this->include_exclude_blogs($args)) {
1232             $blog_filter = 'and category_blog_id '. $blog_filter;
1233        } elseif (isset($args['blog_id'])) {
1234            $blog_filter = 'and category_blog_id = '.intval($args['blog_id']);
1235        }
1236        if (isset($args['parent'])) {
1237            $parent = $args['parent'];
1238            if (is_array($parent)) {
1239                $parent_filter = 'and category_parent in (' . implode(',', $parent) . ')';
1240            } else {
1241                $parent_filter = 'and category_parent = '.intval($parent);
1242            }
1243        }
1244        if (isset($args['category_id'])) {
1245            if (isset($args['children'])) {
1246                if (isset($this->_cat_id_cache['c'.$args['category_id']])) {
1247                    $cat = $this->_cat_id_cache['c'.$args['category_id']];
1248                    if (isset($cat['_children'])) {
1249                        $children = $cat['_children'];
1250                        if ($children === false) {
1251                            return null;
1252                        } else {
1253                            return $children;
1254                        }
1255                    }
1256                }
1257
1258                $cat_filter = 'and category_parent = '.intval($args['category_id']);
1259            } else {
1260                $cat_filter = 'and category_id = '.intval($args['category_id']);
1261                $limit = 1;
1262            }
1263        } elseif (isset($args['label'])) {
1264            $cat_filter = 'and category_label = \''.$this->escape($args['label']).'\'';
1265        } else {
1266            $limit = $args['lastn'];
1267            if (isset($args['sort_order'])) {
1268                if ($args['sort_order'] == 'ascend') {
1269                    $sort_order = 'asc';
1270                } elseif ($args['sort_order'] == 'descend') {
1271                    $sort_order = 'desc';
1272                }
1273            } else {
1274                $sort_order = '';
1275            }
1276        }
1277        $count_column = 'placement_id';
1278        if ($args['show_empty']) {
1279            $join_clause = 'left outer join mt_placement on placement_category_id = category_id';
1280            if (isset($args['entry_id'])) {
1281                $join_clause .= ' left outer join mt_entry on placement_entry_id = entry_id and entry_id = '.intval($args['entry_id']);
1282            } else {
1283                $join_clause .= ' left outer join mt_entry on placement_entry_id = entry_id and entry_status = 2';
1284            }
1285            $count_column = 'entry_id';
1286        } else {
1287            $join_clause = ', mt_entry, mt_placement';
1288            $cat_filter .= ' and placement_category_id = category_id';
1289            if (isset($args['entry_id'])) {
1290                $entry_filter = 'and placement_entry_id = entry_id and placement_entry_id = '.intval($args['entry_id']);
1291            } else {
1292                $entry_filter = 'and placement_entry_id = entry_id and entry_status = 2';
1293            }
1294        }
1295
1296        if (isset($args['class'])) {
1297            $class = $this->escape($args['class']);
1298        } else {
1299            $class = "category";
1300        }
1301        $class_filter = "and category_class='$class'";
1302
1303        $sql = "
1304            select category_id, count($count_column) as category_count
1305              from mt_category $join_clause
1306             where 1 = 1
1307                   $cat_filter
1308                   $entry_filter
1309                   $blog_filter
1310                   $parent_filter
1311                   $class_filter
1312             group by category_id
1313                   <LIMIT>
1314        ";
1315        $sql = $this->apply_limit_sql($sql, $limit, $offset);
1316
1317        $categories = $this->get_results($sql, ARRAY_A);
1318        if (!$categories) {
1319            return null;
1320        }
1321        if (isset($args['children']) && isset($parent_cat)) {
1322            $parent_cat['_children'] =& $categories;
1323        } else {
1324            $ids = array();
1325            $counts = array();
1326            foreach ($categories as $cid => $cat) {
1327                $counts[$cat['category_id']] = $cat['category_count'];
1328                $ids[] = $cat['category_id'];
1329            }
1330            $list = implode(",", $ids);
1331            $sql2 = "
1332                select mt_category.*, mt_trackback.*
1333                    from mt_category left outer join mt_trackback on trackback_category_id = category_id
1334                   where category_id in ($list)
1335                order by category_label $sort_order
1336            ";
1337            $categories = $this->get_results($sql2, ARRAY_A);
1338            $id_list = array();
1339            foreach ($categories as $cid => $cat) {
1340                $cat_id = $cat['category_id'];
1341                $categories[$cid]['category_count'] = $counts[$cat_id];
1342                if (isset($args['top_level_categories']) || !isset($this->_cat_id_cache['c'.$cat_id])) {
1343                    $id_list[] = $cat_id;
1344                    $this->_cat_id_cache['c'.$cat_id] = $categories[$cid];
1345                }
1346                if (isset($args['top_level_categories'])) {
1347                    $this->_cat_id_cache['c'.$cat_id]['_children'] = false;
1348                }
1349            }
1350
1351            $top_cats = array();
1352            foreach ($categories as $cid => $cat) {
1353                if ($cat['category_parent'] > 0) {
1354                    $parent_id = $cat['category_parent'];
1355                    if (isset($this->_cat_id_cache['c'.$parent_id])) {
1356                        if (isset($args['top_level_categories'])) {
1357                            $parent =& $this->fetch_category($categories[$cid]['category_parent']);
1358                            if (!isset($parent['_children']) || ($parent['_children'] === false)) {
1359                                $parent['_children'] = array(&$categories[$cid]);
1360                            } else {
1361                                $parent['_children'][] = $categories[$cid];
1362                            }
1363                        }
1364                    }
1365                }
1366                if ((!$cat['category_parent']) && (isset($args['top_level_categories']))) {
1367                    $top_cats[] = $categories[$cid];
1368                }
1369            }
1370            $this->cache_category_links($id_list);
1371            if (isset($args['top_level_categories'])) {
1372                return $top_cats;
1373            }
1374        }
1375        return $categories;
1376    }
1377
1378    function &fetch_entry($entry_id) {
1379        if (isset($this->_entry_id_cache['entry_id'])) {
1380            return $this->_entry_id_cache[$entry_id];
1381        }
1382        list($entry) = $this->fetch_entries(array('entry_id' => $entry_id));
1383        $this->_entry_id_cache[$entry_id] = $entry;
1384        return $entry;
1385    }
1386
1387    function &fetch_page($entry_id) {
1388        if (isset($this->_entry_id_cache['entry_id'])) {
1389            return $this->_entry_id_cache[$entry_id];
1390        }
1391        list($entry) = $this->fetch_pages(array('entry_id' => $entry_id));
1392        $this->_entry_id_cache[$entry_id] = $entry;
1393        return $entry;
1394    }
1395
1396    function &fetch_author($author_id) {
1397        if (isset($this->_author_id_cache[$author_id])) {
1398            return $this->_author_id_cache[$author_id];
1399        }
1400        global $mt;
1401        $args['blog_id'] = $mt->blog_id;
1402        $args['author_id'] = $author_id;
1403        $args['any_type'] = 1;
1404        $result = $this->fetch_authors($args);
1405        $author = null;
1406        if (is_array($result)) {
1407            $author = $result[0];
1408            $this->_author_id_cache[$author_id] = $author;
1409        }
1410        return $author;
1411    }
1412
1413    function &fetch_author_by_name($author_name) {
1414        global $mt;
1415        $args['blog_id'] = $mt->blog_id;
1416        $args['author_name'] = $this->escape($author_name);
1417        $result = $this->fetch_authors($args);
1418        $author = null;
1419        if (is_array($result)) {
1420            $author = $result[0];
1421            $this->_author_id_cache[$author['author_id']] = $author;
1422        }
1423        return $author;
1424    }
1425
1426    function &fetch_authors($args) {
1427        # Adds blog join
1428        $extend_column = '';
1429        if ($sql = $this->include_exclude_blogs($args)) {
1430            $blog_join = 'join mt_permission on permission_author_id = author_id and permission_blog_id ' . $sql;
1431            $unique_filter = 'distinct';
1432        } elseif (isset($args['blog_id'])) {
1433            $blog_id = intval($args['blog_id']);
1434            $blog_join = "join mt_permission on permission_author_id = author_id and permission_blog_id = $blog_id";
1435        }
1436
1437        # Adds author filter
1438        if (isset($args['author_id'])) {
1439            $author_id = intval($args['author_id']);
1440            $author_filter = " and author_id = $author_id";
1441        }
1442        if (isset($args['author_nickname'])) {
1443            $author_filter .= " and author_nickname = '".$args['author_nickname']."'";
1444        }
1445        if (isset($args['author_name'])) {
1446            $author_filter .= " and author_name = '".$args['author_name']."'";
1447        }
1448
1449        # Adds entry join and filter
1450        if ($args['need_entry']) {
1451            $entry_join = 'join mt_entry on author_id = entry_author_id';
1452            $unique_filter = 'distinct';
1453            $entry_filter = " and entry_status = 2";
1454            if ($blog_join) {
1455                $entry_filter .= " and entry_blog_id = permission_blog_id";
1456            } else {
1457                $entry_filter .= " and entry_blog_id = ".$args['blog_id'];
1458            }
1459        } else {
1460            if ( ! $args['any_type'] )
1461                $author_filter .= " and author_type = 1";
1462        }
1463
1464        # a context hash for filter routines
1465        $ctx = array();
1466        $filters = array();
1467
1468        if (isset($args['status'])) {
1469            $status_arg = $args['status'];
1470            require_once("MTUtil.php");
1471            $status = array(
1472                array('name' => 'enabled', 'id' => 1),
1473                array('name' => 'disabled', 'id' => 2));
1474
1475            $cexpr = create_status_expr_function($status_arg, $status);
1476            if ($cexpr) {
1477                $filters[] = $cexpr;
1478            }
1479        }
1480
1481        if (isset($args['roles']) or isset($args['role'])) {
1482            $role_arg = isset($args['role']) ? $args['role'] : $args['roles'];
1483            require_once("MTUtil.php");
1484            $roles =& $this->fetch_all_roles();
1485            if (!is_array($roles)) $roles = array();
1486
1487            $cexpr = create_role_expr_function($role_arg, $roles);
1488            if ($cexpr) {
1489                $rmap = array();
1490                $role_list = array();
1491                foreach ($roles as $role) {
1492                    $role_list[] = $role['role_id'];
1493                }
1494                $as =& $this->fetch_associations(array('blog_id' => $blog_id, 'role_id' => $role_list));
1495                if ($as) {
1496                    foreach ($as as $a) {
1497                        $rmap[$a['association_author_id']][$a['association_role_id']]++;
1498                    }
1499                }
1500                $ctx['r'] =& $rmap;
1501                $filters[] = $cexpr;
1502            }
1503        }
1504
1505        # Adds a score or rate filter to the filters list.
1506        $re_sort = false;
1507        if (isset($args['namespace'])) {
1508            require_once("MTUtil.php");
1509            $arg_names = array('min_score', 'max_score', 'min_rate', 'max_rate', 'min_count', 'max_count' );
1510            foreach ($arg_names as $n) {
1511                if (isset($args[$n])) {
1512                    $rating_args = $args[$n];
1513                    $cexpr = create_rating_expr_function($rating_args, $n, $args['namespace'], 'author');
1514                    if ($cexpr) {
1515                        $filters[] = $cexpr;
1516                        $re_sort = true;
1517                    } else {
1518                        return null;
1519                    }
1520                }
1521            }
1522        }
1523
1524        # sort
1525        $join_score = "";
1526        if (isset($args['sort_by'])) {
1527            if (($args['sort_by'] == 'score') || ($args['sort_by'] == 'rate')) {
1528                $join_score = "join mt_objectscore on objectscore_object_id = author_id and objectscore_namespace='".$args['namespace']."' and objectscore_object_ds='author'";
1529                $unique_filter = 'distinct';
1530                $order_sql = "order by author_created_on desc";
1531                $re_sort = true;
1532            } else {
1533                $sort_col = $args['sort_by'];
1534                $order = '';
1535                if (isset($args['sort_order'])) {
1536                    if ($args['sort_order'] == 'ascend')
1537                        $order = 'asc';
1538                    else
1539                        $order = 'desc';
1540                }
1541                $order_sql = "order by $sort_col $order";
1542   
1543                if (isset($args['start_string'])) {
1544                    $val = $args['start_string'];
1545                    if ($order == 'asc')
1546                        $val_order = '>';
1547                    else
1548                        $val_order = '<';
1549                    $sort_filter =  " and $sort_col $val_order '$val'";
1550                }
1551   
1552                if (isset($args['start_num'])) {
1553                    $val = $args['start_num'];
1554                    if ($order == 'asc')
1555                        $val_order = '>';
1556                    else
1557                        $val_order = '<';
1558                    $sort_filter .= " and $sort_col $val_order $val";
1559                }
1560            }
1561        }
1562
1563        $limit = 0;
1564        $offset = 0;
1565        if (isset($args['lastn']))
1566            $limit = $args['lastn'];
1567        if (isset($args['offset']))
1568            $limit = $args['offset'];
1569
1570        if ($re_sort) {
1571            $post_select_limit = $limit;
1572            $post_select_offset = $offset;
1573            $limit = 0; $offset = 0;
1574        }
1575       
1576        $sql = "
1577            select $unique_filter
1578                   mt_author.*
1579                   $extend_column
1580              from mt_author
1581                   $blog_join
1582                   $entry_join
1583                   $join_score
1584              where 1 = 1
1585                $author_filter
1586                $entry_filter
1587                $sort_filter
1588              $order_sql
1589                   <LIMIT>
1590        ";
1591        $sql = $this->apply_limit_sql($sql, $limit, $offset);
1592
1593        $result = $this->query_start($sql);
1594        if (!$result) return null;
1595
1596        $authors = array();
1597        if ($args['sort_by'] != 'score' && $args['sort_by'] != 'rate') {
1598            $offset = $post_select_offset ? $post_select_offset : 0;
1599            $limit = $post_select_limit ? $post_select_limit : 0;
1600        }
1601        $j = 0;
1602        while (true) {
1603            $e = $this->query_fetch(ARRAY_A);
1604            if ($offset && ($j++ < $offset)) continue;
1605            if (!isset($e)) break;
1606            if (count($filters)) {
1607                foreach ($filters as $f) {
1608                    if (!$f($e, $ctx)) continue 2;
1609                }
1610            }
1611            $authors[] = $e;
1612            if (($limit > 0) && (count($authors) >= $limit)) break;
1613        }
1614
1615        if (isset($args['sort_by']) && ('score' == $args['sort_by'])) {
1616            $authors_tmp = array();
1617            $order = 'asc';
1618            if (isset($args['sort_order']))
1619                $order = $args['sort_order'] == 'ascend' ? 'asc' : 'desc';
1620            foreach ($authors as $a) {
1621                $authors_tmp[$a['author_id']] = $a;
1622            }
1623            $scores = $this->fetch_sum_scores($args['namespace'], 'author', $order,
1624                $author_filter
1625            );
1626            $offset = $post_select_offset ? $post_select_offset : 0;
1627            $limit = $post_select_limit ? $post_select_limit : 0;
1628            $j = 0;
1629            $authors_sorted = array();
1630            foreach($scores as $score) {
1631                if (array_key_exists($score['objectscore_object_id'], $authors_tmp)) {
1632                    if ($offset && ($j++ < $offset)) continue;
1633                    array_push($authors_sorted, $authors_tmp[$score['objectscore_object_id']]);
1634                    unset($authors_tmp[$score['objectscore_object_id']]);
1635                    if (($limit > 0) && (count($authors_sorted) >= $limit)) break;
1636                }
1637            }
1638            $authors = $authors_sorted;
1639
1640        } elseif (isset($args['sort_by']) && ('rate' == $args['sort_by'])) {
1641            $authors_tmp = array();
1642            $order = 'asc';
1643            if (isset($args['sort_order']))
1644                $order = $args['sort_order'] == 'ascend' ? 'asc' : 'desc';
1645            foreach ($authors as $a) {
1646                $authors_tmp[$a['author_id']] = $a;
1647            }
1648            $scores = $this->fetch_avg_scores($args['namespace'], 'author', $order,
1649                $author_filter
1650            );
1651            $offset = $post_select_offset ? $post_select_offset : 0;
1652            $limit = $post_select_limit ? $post_select_limit : 0;
1653            $j = 0;
1654            $authors_sorted = array();
1655            foreach($scores as $score) {
1656                if (array_key_exists($score['objectscore_object_id'], $authors_tmp)) {
1657                    if ($offset && ($j++ < $offset)) continue;
1658                    array_push($authors_sorted, $authors_tmp[$score['objectscore_object_id']]);
1659                    unset($authors_tmp[$score['objectscore_object_id']]);
1660                    if (($limit > 0) && (count($authors_sorted) >= $limit)) break;
1661                }
1662            }
1663            $authors = $authors_sorted;
1664
1665        }
1666
1667        return $authors;
1668    }
1669
1670    function &fetch_permission($args) {
1671        if ($sql = $this->include_exclude_blogs($args)) {
1672            $blog_filter = 'permission_blog_id ' . $sql;
1673        } elseif (isset($args['blog_id'])) {
1674            $blog_id = intval($args['blog_id']);
1675            $blog_filter = "and permission_blog_id = $blog_id";
1676        }
1677        if (isset($args['id'])) {
1678          $id_filter = 'and permission_author_id in ('.$args['id'].')';
1679        }
1680
1681        $sql = "select
1682                    *
1683                from
1684                    mt_permission
1685                where
1686                    1 = 1
1687                    $blog_filter
1688                    $id_filter";
1689
1690        $result = $this->get_results($sql, ARRAY_A);
1691        return $result;
1692    }
1693
1694    function &fetch_all_roles() {
1695        $sql = "select *
1696                  from mt_role
1697              order by role_name";
1698        $result = $this->get_results($sql, ARRAY_A);
1699        return $result;
1700    }
1701
1702    function &fetch_associations($args) {
1703        $id_list = implode(",", $args['role_id']);
1704        if (empty($id_list))
1705            return;
1706        if ($sql = $this->include_exclude_blogs($args)) {
1707            $blog_filter = 'and association_blog_id  ' . $sql;
1708        } elseif (isset($args['blog_id'])) {
1709            $blog_filter = 'and association_blog_id = ' . intval($args['blog_id']);
1710        }
1711        $sql = "select *
1712                  from mt_association
1713                 where association_role_id in ($id_list)
1714                   $blog_filter";
1715        $result = $this->get_results($sql, ARRAY_A);
1716        return $result;
1717    }
1718
1719    function &fetch_tag($tag_id) {
1720        $tag_id = intval($tag_id);
1721        if (isset($this->_tag_id_cache[$tag_id])) {
1722            return $this->_tag_id_cache[$tag_id];
1723        }
1724        $tag = $this->get_row("
1725            select *
1726              from mt_tag
1727             where tag_id=$tag_id
1728        ", ARRAY_A);
1729        $this->_tag_id_cache[$tag_id] = $tag;
1730        return $tag;
1731    }
1732
1733    function &fetch_tag_by_name($tag_name) {
1734        $tag_name = $this->escape($tag_name);
1735        $tag = $this->get_row("
1736            select *
1737              from mt_tag
1738             where tag_name='$tag_name'
1739        ", ARRAY_A);
1740        $this->_tag_id_cache[$tag['tag_id']] = $tag;
1741        return $tag;
1742    }
1743
1744    function fetch_scores($namespace, $obj_id, $datasource) {
1745        $scores = $this->get_results("
1746            select * from mt_objectscore
1747            where objectscore_namespace='$namespace'
1748            and objectscore_object_id='$obj_id'
1749            and objectscore_object_ds='$datasource'
1750        ", ARRAY_A);
1751        return $scores;
1752    }
1753
1754    function fetch_score($namespace, $obj_id, $user_id, $datasource) {
1755        list($score) = $this->get_results("
1756            select * from mt_objectscore
1757            where objectscore_namespace='$namespace'
1758            and objectscore_object_id='$obj_id'
1759            and objectscore_object_ds='$datasource'
1760            and objectscore_author_id='$user_id'
1761        ", ARRAY_A);
1762        return $score;
1763    }
1764
1765    function fetch_sum_scores($namespace, $datasource, $order, $filters) {
1766        $othertables = '';
1767        $otherwhere = '';
1768        if ($datasource == 'asset') {
1769            $othertables = ', mt_author';
1770            $otherwhere = 'AND (objectscore_author_id = author_id)';
1771        }
1772        $join_column = $datasource . '_id';
1773        $join_where = "AND ($join_column = objectscore_object_id)";
1774        $sql_scores = 
1775            "SELECT SUM(objectscore_score) AS sum_objectscore_score, objectscore_object_id
1776             FROM mt_objectscore, mt_$datasource $othertables
1777             WHERE (objectscore_namespace = '$namespace')
1778             AND (objectscore_object_ds = '$datasource')
1779             $join_where
1780             $otherwhere
1781             $filters
1782             GROUP BY objectscore_object_id
1783             ORDER BY sum_objectscore_score " . $order;
1784        $scores = $this->get_results($sql_scores, ARRAY_A);
1785        return $scores;
1786    }
1787
1788    function fetch_avg_scores($namespace, $datasource, $order, $filters) {
1789        $othertables = '';
1790        $otherwhere = '';
1791        if ($datasource == 'asset') {
1792            $othertables = ', mt_author';
1793            $otherwhere = 'AND (objectscore_author_id = author_id)';
1794        }
1795        $join_column = $datasource . '_id';
1796        $join_where = "AND ($join_column = objectscore_object_id)";
1797        $sql_scores = 
1798            "SELECT AVG(objectscore_score) AS sum_objectscore_score, objectscore_object_id
1799             FROM mt_objectscore, mt_$datasource $othertables
1800             WHERE (objectscore_namespace = '$namespace')
1801             AND (objectscore_object_ds = '$datasource')
1802             $join_where
1803             $otherwhere
1804             $filters
1805             GROUP BY objectscore_object_id
1806             ORDER BY sum_objectscore_score " . $order;
1807        $scores = $this->get_results($sql_scores, ARRAY_A);
1808        return $scores;
1809    }
1810
1811    function cache_permalinks(&$entry_list) {
1812        $id_list = '';
1813        foreach ($entry_list as $entry_id) {
1814            if (!isset($this->_entry_link_cache[$entry_id.';Individual'])) {
1815                $id_list .= ','.$entry_id;
1816                $this->_entry_link_cache[$entry_id.';Individual'] = ''; 
1817            }
1818        }
1819        if (empty($id_list))
1820            return;
1821        $id_list = substr($id_list, 1);
1822        $query = "
1823            select fileinfo_entry_id, fileinfo_url, blog_site_url, blog_file_extension
1824              from mt_fileinfo, mt_templatemap, mt_blog
1825             where fileinfo_entry_id in ($id_list)
1826               and fileinfo_archive_type = 'Individual'
1827               and blog_id = fileinfo_blog_id
1828               and templatemap_id = fileinfo_templatemap_id
1829               and templatemap_is_preferred = 1
1830        ";
1831        $results = $this->get_results($query, ARRAY_N);
1832        if ($results) {
1833
1834            foreach ($results as $row) {
1835                $blog_url = $row[2];
1836                $blog_url = preg_replace('!(https?://(?:[^/]+))/.*!', '$1', $blog_url);
1837                $url = $blog_url . $row[1];
1838                $url = _strip_index($url, array('blog_file_extension' => $row[3]));
1839                $this->_entry_link_cache[$row[0].';Individual'] = $url;
1840            }
1841        }
1842    }
1843
1844    function cache_category_links(&$cat_list) {
1845        $id_list = '';
1846        foreach ($cat_list as $cat_id) {
1847            if (!isset($this->_cat_link_cache[$cat_id])) {
1848                $id_list .= ','.$cat_id;
1849                $this->_cat_link_cache[$cat_id] = '';
1850            }
1851        }
1852        if (empty($id_list))
1853            return;
1854        $id_list = substr($id_list, 1);
1855        $query = "
1856            select fileinfo_category_id, fileinfo_url, blog_site_url, blog_file_extension
1857              from mt_fileinfo, mt_templatemap, mt_blog
1858             where fileinfo_category_id in ($id_list)
1859               and fileinfo_archive_type = 'Category'
1860               and blog_id = fileinfo_blog_id
1861               and templatemap_id = fileinfo_templatemap_id
1862               and templatemap_is_preferred = 1
1863        ";
1864        $results = $this->get_results($query, ARRAY_N);
1865        if ($results) {
1866            foreach ($results as $row) {
1867                $blog_url = $row[2];
1868                $blog_url = preg_replace('!(https?://(?:[^/]+))/.*!', '$1', $blog_url);
1869                $url = $blog_url . $row[1];
1870                $url = _strip_index($url, array('blog_file_extension' => $row[3]));
1871                $this->_cat_link_cache[$row[0]] = $url;
1872            }
1873        }
1874    }
1875
1876    function cache_comment_counts(&$entry_list) {
1877        $id_list = '';
1878        foreach ($entry_list as $entry_id) {
1879            if (!isset($this->_comment_count_cache[$entry_id])) {
1880                $id_list .= ','.$entry_id;
1881                $this->_comment_count_cache[$entry_id] = 0;
1882            }
1883        }
1884        if (empty($id_list))
1885            return;
1886        $id_list = substr($id_list, 1);
1887        $query = "
1888            select entry_id, entry_comment_count
1889              from mt_entry
1890             where entry_id in ($id_list)
1891        ";
1892        $results = $this->get_results($query, ARRAY_N);
1893        if ($results) {
1894            foreach ($results as $row) {
1895                $this->_comment_count_cache[$row[0]] = $row[1];
1896            }
1897        }
1898    }
1899
1900    function blog_entry_count($args) {
1901
1902        if ($sql = $this->include_exclude_blogs($args)) {
1903            $blog_filter = 'and entry_blog_id ' . $sql;
1904        } elseif (isset($args['blog_id'])) {
1905            $blog_id = intval($args['blog_id']);
1906            $blog_filter = 'and entry_blog_id = ' . $blog_id;
1907        }
1908        $class = 'entry';
1909        if (isset($args['class'])) {
1910            $class = $args['class'];
1911        }
1912        $count = $this->get_var("
1913          select count(*)
1914            from mt_entry
1915            where entry_status = 2
1916            and entry_class='$class'
1917            $blog_filter
1918            ");
1919        return $count;
1920    }
1921
1922    function blog_comment_count($args) {
1923
1924        if ($sql = $this->include_exclude_blogs($args)) {
1925            $blog_filter = 'and comment_blog_id ' . $sql;
1926        } elseif (isset($args['blog_id'])) {
1927            $blog_id = intval($args['blog_id']);
1928            $blog_filter = 'and comment_blog_id = ' . $blog_id;
1929        }
1930
1931        $count = $this->get_var("
1932            select count(*)
1933              from mt_entry, mt_comment
1934             where entry_status = 2
1935               and comment_visible = 1
1936               and comment_entry_id = entry_id
1937               $blog_filter
1938        ");
1939        return $count;
1940    }
1941
1942    function category_comment_count($args) {
1943        $cat_id = (int)$args['category_id'];
1944        $sql = "select count(*)
1945             from mt_placement, mt_comment, mt_entry
1946            where placement_category_id=$cat_id
1947              and entry_id=placement_entry_id
1948              and entry_status=2
1949              and comment_entry_id=entry_id
1950              and comment_visible=1";
1951        return $this->get_var($sql);
1952    }
1953
1954    function blog_ping_count($args) {
1955
1956        if ($sql = $this->include_exclude_blogs($args)) {
1957            $blog_filter = 'and tbping_blog_id ' . $sql;
1958        } elseif (isset($args['blog_id'])) {
1959            $blog_id = intval($args['blog_id']);
1960            $blog_filter = 'and tbping_blog_id = ' . $blog_id;
1961        }
1962
1963        $count = $this->get_var("
1964            select count(*)
1965              from mt_tbping, mt_trackback
1966             where tbping_visible = 1
1967               and tbping_tb_id = trackback_id
1968                   $blog_filter
1969        ");
1970        return $count;
1971    }
1972
1973    function blog_category_count($args) {
1974
1975        if ($sql = $this->include_exclude_blogs($args)) {
1976            $blog_filter = 'and category_blog_id ' . $sql;
1977        } elseif (isset($args['blog_id'])) {
1978            $blog_id = intval($args['blog_id']);
1979            $blog_filter = 'and category_blog_id = ' . $blog_id;
1980        }
1981        $count = $this->get_var("
1982            select count(*)
1983              from mt_category
1984             where 1 = 1
1985             $blog_filter
1986        ");
1987        return $count;
1988    }
1989
1990    function tags_entry_count($tag_id, $class = 'entry') {
1991        $count = $this->get_var("
1992          select count(*)
1993            from mt_objecttag, mt_entry
1994           where objecttag_tag_id = " . intval($tag_id) . "
1995             and entry_id = objecttag_object_id and objecttag_object_datasource='entry'
1996             and entry_status = 2
1997             and entry_class = '$class'
1998        ");
1999        return $count;
2000    }
2001
2002    function entry_comment_count($entry_id) {
2003        if (isset($this->_comment_count_cache[$entry_id])) {
2004            return $this->_comment_count_cache[$entry_id];
2005        }
2006        $entry = $this->fetch_entry($entry_id);
2007        $count = $entry['entry_comment_count'];
2008        $this->_comment_count_cache[$entry_id] = $count;
2009        return $count;
2010    }
2011
2012    function author_entry_count($args) {
2013        if ($sql = $this->include_exclude_blogs($args)) {
2014            $blog_filter = 'and entry_blog_id ' . $sql;
2015        } elseif (isset($args['blog_id'])) {
2016            $blog_id = intval($args['blog_id']);
2017            $blog_filter = 'and entry_blog_id = ' . $blog_id;
2018        }
2019        if (isset($args['author_id'])) {
2020            $author_id = intval($args['author_id']);
2021            $author_filter = " and entry_author_id = $author_id";
2022        }
2023        if (isset($args['class'])) {
2024            $class = $args['class'];
2025        }
2026        $count = $this->get_var("
2027          select count(*)
2028            from mt_entry
2029            where entry_status = 2
2030            and entry_class='$class'
2031            $blog_filter
2032            $author_filter
2033            ");
2034        return $count;
2035    }
2036
2037    function &fetch_placements($args) {
2038        $category_id_list = $args['category_id'];
2039        $id_list = '';
2040        foreach ($category_id_list as $cat_id) {
2041            $id_list .= ',' . $cat_id;
2042        }
2043        if (empty($id_list))
2044            return;
2045        $id_list = substr($id_list, 1);
2046        $sql = "
2047            select mt_placement.*
2048              from mt_placement, mt_entry
2049              where placement_category_id in ($id_list)
2050               and entry_id = placement_entry_id and entry_status = 2
2051        ";
2052        $results = $this->get_results($sql, ARRAY_A);
2053        return $results;
2054    }
2055
2056    function &fetch_objecttags($args) {
2057        $tag_id_list = $args['tag_id'];
2058        $id_list = '';
2059        foreach ($tag_id_list as $tag_id) {
2060            $id_list .= ',' . $tag_id;
2061        }
2062        if (empty($id_list))
2063            return;
2064        $id_list = substr($id_list, 1);
2065
2066        $blog_filter = $this->include_exclude_blogs($args);
2067        if ($blog_filter == '' and $args['blog_id'])
2068            $blog_filter = intval($args['blog_id']);
2069        if ($blog_filter != '') 
2070            $blog_filter = 'and objecttag_blog_id = ' . $blog_filter;
2071
2072        if (isset($args['datasource']) && strtolower($args['datasource']) == 'asset') {
2073            $datasource = $args['datasource'];
2074            $from_object = 'mt_asset';
2075            $object_filter = 'and asset_id = objecttag_object_id';
2076        } else {
2077            $datasource = 'entry';
2078            $from_object = 'mt_entry';
2079            $object_filter = 'and entry_id = objecttag_object_id and entry_status = 2';
2080        }
2081        $sql = "
2082            select mt_objecttag.*
2083              from mt_objecttag, $from_object
2084              where
2085                objecttag_object_datasource ='$datasource'
2086                and objecttag_tag_id in ($id_list)
2087                $blog_filter
2088                $object_filter
2089        ";
2090        $results = $this->get_results($sql, ARRAY_A);
2091        return $results;
2092    }
2093
2094    function &fetch_comments($args) {
2095        # load comments
2096        $entry_id = intval($args['entry_id']);
2097
2098        $sql = $this->include_exclude_blogs($args);
2099        if ($sql != '') {
2100            $blog_filter = 'and comment_blog_id ' . $sql;
2101            if (isset($args['blog_id']))
2102                $blog =& $this->fetch_blog($args['blog_id']);
2103        } elseif ($args['blog_id']) {
2104            $blog =& $this->fetch_blog($args['blog_id']);
2105            $blog_filter = ' and comment_blog_id = ' . $blog['blog_id'];
2106        }
2107
2108        # Adds a score or rate filter to the filters list.
2109        if (isset($args['namespace'])) {
2110            require_once("MTUtil.php");
2111            $arg_names = array('min_score', 'max_score', 'min_rate', 'max_rate', 'min_count', 'max_count' );
2112            foreach ($arg_names as $n) {
2113                if (isset($args[$n])) {
2114                    $comment_args = $args[$n];
2115                    $cexpr = create_rating_expr_function($comment_args, $n, $args['namespace'], 'comment');
2116                    if ($cexpr) {
2117                        $filters[] = $cexpr;
2118                    } else {
2119                        return null;
2120                    }
2121                }
2122            }
2123            if (isset($args['scored_by'])) {
2124                $voter = $this->fetch_author_by_name($args['scored_by']);
2125                if (!$voter) {
2126                    echo "Invalid scored by filter: ".$args['scored_by'];
2127                    return null;
2128                }
2129                $cexpr = create_rating_expr_function($voter['author_id'], 'scored_by', $args['namespace'], 'comment');
2130                if ($cexpr) {
2131                    $filters[] = $cexpr;
2132                } else {
2133                    return null;
2134                }
2135            }
2136        }
2137
2138        $order = $query_order = 'desc';
2139        if (isset($args['sort_order'])) {
2140            if ($args['sort_order'] == 'ascend') {
2141                $order = $query_order = 'asc';
2142            }
2143        } elseif (isset($blog) && isset($blog['blog_sort_order_comments'])) {
2144            if ($blog['blog_sort_order_comments'] == 'ascend') {
2145                $order = $query_order = 'asc';
2146            }
2147        }
2148        if ($order == 'asc' && (isset($args['lastn']) || isset($args['offset']))) {
2149            $reorder = 1;
2150            $query_order = 'desc';
2151        }
2152
2153        if ($entry_id) {
2154            $entry_filter = " and comment_entry_id = $entry_id";
2155            $entry_join = "join mt_entry on entry_id = comment_entry_id";
2156        } else {
2157            $entry_join = "join mt_entry on entry_id = comment_entry_id and entry_status = 2";
2158        }
2159
2160        $join_score = "";
2161        $distinct = "";
2162        if ( isset($args['sort_by'])
2163          && (($args['sort_by'] == 'score') || ($args['sort_by'] == 'rate')) ) {
2164            $join_score = "join mt_objectscore on objectscore_object_id = comment_id and objectscore_namespace='".$args['namespace']."' and objectscore_object_ds='comment'";
2165            $distinct = " distinct";
2166        }
2167
2168        $limit = 0;
2169        $offset = 0;
2170        if (isset($args['lastn']))
2171            $limit = $args['lastn'];
2172        if (isset($args['limit']))
2173            $limit = $args['limit'];
2174        if (isset($args['offset']))
2175            $offset = $args['offset'];
2176        if (count($filters)) {
2177            $post_select_limit = $limit;
2178            $post_select_offset = $offset;
2179            $limit = 0; $offset = 0;
2180        }
2181
2182        $sql = "
2183            select $distinct
2184                   mt_comment.*,
2185                   mt_entry.*
2186              from mt_comment
2187                   $entry_join
2188                   $join_score
2189             where comment_visible = 1
2190                   $entry_filter
2191                   $blog_filter
2192             order by comment_created_on $query_order
2193                   <LIMIT>";
2194        $sql = $this->apply_limit_sql($sql, $limit, $offset);
2195
2196        # Fetch resultset
2197        $result = $this->query_start($sql);
2198        if (!$result) return null;
2199
2200        $comments = array();
2201        $j = 0;
2202        while (true) {
2203            $e = $this->query_fetch(ARRAY_A);
2204            if (!isset($e)) break;
2205            if (count($filters)) {
2206                foreach ($filters as $f) {
2207                    if (!$f($e, $ctx)) continue 2;
2208                }
2209                if ($post_select_offset && ($j++ < $post_select_offset)) continue;
2210<