Dotclear

source: inc/core/class.dc.blog.php @ 3730:5c45a5df9a59

Revision 3730:5c45a5df9a59, 78.6 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Code formatting (PSR-2)

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) {return;}
13
14/**
15@ingroup DC_CORE
16@nosubgrouping
17@brief Dotclear blog class.
18
19Dotclear blog class instance is provided by dcCore $blog property.
20 */
21class dcBlog
22{
23    /** @var dcCore dcCore instance */
24    protected $core;
25    /** @var connection Database connection object */
26    public $con;
27    /** @var string Database table prefix */
28    public $prefix;
29
30    /** @var string Blog ID */
31    public $id;
32    /** @var string Blog unique ID */
33    public $uid;
34    /** @var string Blog name */
35    public $name;
36    /** @var string Blog description */
37    public $desc;
38    /** @var string Blog URL */
39    public $url;
40    /** @var string Blog host */
41    public $host;
42    /** @var string Blog creation date */
43    public $creadt;
44    /** @var string Blog last update date */
45    public $upddt;
46    /** @var string Blog status */
47    public $status;
48
49    /** @var dcSettings dcSettings object */
50    public $settings;
51    /** @var string Blog theme path */
52    public $themes_path;
53    /** @var string Blog public path */
54    public $public_path;
55
56    private $post_status    = array();
57    private $comment_status = array();
58
59    private $categories;
60
61    /** @var boolean Disallow entries password protection */
62    public $without_password = true;
63
64    /**
65    Inits dcBlog object
66
67    @param    core        <b>dcCore</b>        Dotclear core reference
68    @param    id        <b>string</b>        Blog ID
69     */
70    public function __construct($core, $id)
71    {
72        $this->con    = &$core->con;
73        $this->prefix = $core->prefix;
74        $this->core   = &$core;
75
76        if (($b = $this->core->getBlog($id)) !== false) {
77            $this->id     = $id;
78            $this->uid    = $b->blog_uid;
79            $this->name   = $b->blog_name;
80            $this->desc   = $b->blog_desc;
81            $this->url    = $b->blog_url;
82            $this->host   = http::getHostFromURL($this->url);
83            $this->creadt = strtotime($b->blog_creadt);
84            $this->upddt  = strtotime($b->blog_upddt);
85            $this->status = $b->blog_status;
86
87            $this->settings = new dcSettings($this->core, $this->id);
88
89            $this->themes_path = path::fullFromRoot($this->settings->system->themes_path, DC_ROOT);
90            $this->public_path = path::fullFromRoot($this->settings->system->public_path, DC_ROOT);
91
92            $this->post_status['-2'] = __('Pending');
93            $this->post_status['-1'] = __('Scheduled');
94            $this->post_status['0']  = __('Unpublished');
95            $this->post_status['1']  = __('Published');
96
97            $this->comment_status['-2'] = __('Junk');
98            $this->comment_status['-1'] = __('Pending');
99            $this->comment_status['0']  = __('Unpublished');
100            $this->comment_status['1']  = __('Published');
101
102            # --BEHAVIOR-- coreBlogConstruct
103            $this->core->callBehavior('coreBlogConstruct', $this);
104        }
105    }
106
107    /// @name Common public methods
108    //@{
109    /**
110    Returns blog URL ending with a question mark.
111     */
112    public function getQmarkURL()
113    {
114        if (substr($this->url, -1) != '?') {
115            return $this->url . '?';
116        }
117
118        return $this->url;
119    }
120
121    /**
122    Returns jQuery version selected for the blog.
123     */
124    public function getJsJQuery()
125    {
126        $version = $this->settings->system->jquery_version;
127        if ($version == '') {
128            $version = DC_DEFAULT_JQUERY; // defined in inc/prepend.php
129        }
130        return 'jquery/' . $version;
131    }
132
133    /**
134    Returns public URL of specified plugin file.
135     */
136    public function getPF($pf, $strip_host = true)
137    {
138        $ret = $this->getQmarkURL() . 'pf=' . $pf;
139        if ($strip_host) {
140            $ret = html::stripHostURL($ret);
141        }
142        return $ret;
143    }
144
145    /**
146    Returns public URL of specified var file.
147     */
148    public function getVF($vf, $strip_host = true)
149    {
150        $ret = $this->getQmarkURL() . 'vf=' . $vf;
151        if ($strip_host) {
152            $ret = html::stripHostURL($ret);
153        }
154        return $ret;
155    }
156
157    /**
158    Returns an entry status name given to a code. Status are translated, never
159    use it for tests. If status code does not exist, returns <i>unpublished</i>.
160
161    @param    s    <b>integer</b> Status code
162    @return    <b>string</b> Blog status name
163     */
164    public function getPostStatus($s)
165    {
166        if (isset($this->post_status[$s])) {
167            return $this->post_status[$s];
168        }
169        return $this->post_status['0'];
170    }
171
172    /**
173    Returns an array of available entry status codes and names.
174
175    @return    <b>array</b> Simple array with codes in keys and names in value
176     */
177    public function getAllPostStatus()
178    {
179        return $this->post_status;
180    }
181
182    /**
183    Returns an array of available comment status codes and names.
184
185    @return    <b>array</b> Simple array with codes in keys and names in value
186     */
187    public function getAllCommentStatus()
188    {
189        return $this->comment_status;
190    }
191
192    /**
193    Disallows entries password protection. You need to set it to
194    <var>false</var> while serving a public blog.
195
196    @param    v        <b>boolean</b>
197     */
198    public function withoutPassword($v)
199    {
200        $this->without_password = (boolean) $v;
201    }
202    //@}
203
204    /// @name Triggers methods
205    //@{
206    /**
207    Updates blog last update date. Should be called every time you change
208    an element related to the blog.
209     */
210    public function triggerBlog()
211    {
212        $cur = $this->con->openCursor($this->prefix . 'blog');
213
214        $cur->blog_upddt = date('Y-m-d H:i:s');
215
216        $cur->update("WHERE blog_id = '" . $this->con->escape($this->id) . "' ");
217
218        # --BEHAVIOR-- coreBlogAfterTriggerBlog
219        $this->core->callBehavior('coreBlogAfterTriggerBlog', $cur);
220    }
221
222    /**
223    Updates comment and trackback counters in post table. Should be called
224    every time a comment or trackback is added, removed or changed its status.
225
226    @param    id        <b>integer</b>        Comment ID
227    @param    del        <b>boolean</b>        If comment is delete, set this to true
228     */
229    public function triggerComment($id, $del = false)
230    {
231        $this->triggerComments($id, $del);
232    }
233
234    /**
235    Updates comments and trackbacks counters in post table. Should be called
236    every time comments or trackbacks are added, removed or changed their status.
237
238    @param    ids        <b>mixed</b>        Comment(s) ID(s)
239    @param    del        <b>boolean</b>        If comment is delete, set this to true
240    @param    affected_posts        <b>mixed</b>        Posts(s) ID(s)
241     */
242    public function triggerComments($ids, $del = false, $affected_posts = null)
243    {
244        $comments_ids = dcUtils::cleanIds($ids);
245
246        # Get posts affected by comments edition
247        if (empty($affected_posts)) {
248            $strReq =
249            'SELECT post_id ' .
250            'FROM ' . $this->prefix . 'comment ' .
251            'WHERE comment_id' . $this->con->in($comments_ids) .
252                'GROUP BY post_id';
253
254            $rs = $this->con->select($strReq);
255
256            $affected_posts = array();
257            while ($rs->fetch()) {
258                $affected_posts[] = (integer) $rs->post_id;
259            }
260        }
261
262        if (!is_array($affected_posts) || empty($affected_posts)) {
263            return;
264        }
265
266        # Count number of comments if exists for affected posts
267        $strReq =
268        'SELECT post_id, COUNT(post_id) AS nb_comment, comment_trackback ' .
269        'FROM ' . $this->prefix . 'comment ' .
270        'WHERE comment_status = 1 ' .
271        'AND post_id' . $this->con->in($affected_posts) .
272            'GROUP BY post_id,comment_trackback';
273
274        $rs = $this->con->select($strReq);
275
276        $posts = array();
277        while ($rs->fetch()) {
278            if ($rs->comment_trackback) {
279                $posts[$rs->post_id]['trackback'] = $rs->nb_comment;
280            } else {
281                $posts[$rs->post_id]['comment'] = $rs->nb_comment;
282            }
283        }
284
285        # Update number of comments on affected posts
286        $cur = $this->con->openCursor($this->prefix . 'post');
287        foreach ($affected_posts as $post_id) {
288            $cur->clean();
289
290            if (!array_key_exists($post_id, $posts)) {
291                $cur->nb_trackback = 0;
292                $cur->nb_comment   = 0;
293            } else {
294                $cur->nb_trackback = empty($posts[$post_id]['trackback']) ? 0 : $posts[$post_id]['trackback'];
295                $cur->nb_comment   = empty($posts[$post_id]['comment']) ? 0 : $posts[$post_id]['comment'];
296            }
297
298            $cur->update('WHERE post_id = ' . $post_id);
299        }
300    }
301    //@}
302
303    /// @name Categories management methods
304    //@{
305    public function categories()
306    {
307        if (!($this->categories instanceof dcCategories)) {
308            $this->categories = new dcCategories($this->core);
309        }
310
311        return $this->categories;
312    }
313
314    /**
315    Retrieves categories. <var>$params</var> is an associative array which can
316    take the following parameters:
317
318    - post_type: Get only entries with given type (default "post")
319    - cat_url: filter on cat_url field
320    - cat_id: filter on cat_id field
321    - start: start with a given category
322    - level: categories level to retrieve
323
324    @param    params    <b>array</b>        Parameters
325    @return    <b>record</b>
326     */
327    public function getCategories($params = array())
328    {
329        $c_params = array();
330        if (isset($params['post_type'])) {
331            $c_params['post_type'] = $params['post_type'];
332            unset($params['post_type']);
333        }
334        $counter = $this->getCategoriesCounter($c_params);
335
336        if (isset($params['without_empty']) && ($params['without_empty'] == false)) {
337            $without_empty = false;
338        } else {
339            $without_empty = $this->core->auth->userID() == false; # Get all categories if in admin display
340        }
341
342        $start = isset($params['start']) ? (integer) $params['start'] : 0;
343        $l     = isset($params['level']) ? (integer) $params['level'] : 0;
344
345        $rs = $this->categories()->getChildren($start, null, 'desc');
346
347        # Get each categories total posts count
348        $data  = array();
349        $stack = array();
350        $level = 0;
351        $cols  = $rs->columns();
352        while ($rs->fetch()) {
353            $nb_post = isset($counter[$rs->cat_id]) ? (integer) $counter[$rs->cat_id] : 0;
354
355            if ($rs->level > $level) {
356                $nb_total          = $nb_post;
357                $stack[$rs->level] = (integer) $nb_post;
358            } elseif ($rs->level == $level) {
359                $nb_total = $nb_post;
360                $stack[$rs->level] += $nb_post;
361            } else {
362                $nb_total = $stack[$rs->level + 1] + $nb_post;
363                if (isset($stack[$rs->level])) {
364                    $stack[$rs->level] += $nb_total;
365                } else {
366                    $stack[$rs->level] = $nb_total;
367                }
368                unset($stack[$rs->level + 1]);
369            }
370
371            if ($nb_total == 0 && $without_empty) {
372                continue;
373            }
374
375            $level = $rs->level;
376
377            $t = array();
378            foreach ($cols as $c) {
379                $t[$c] = $rs->f($c);
380            }
381            $t['nb_post']  = $nb_post;
382            $t['nb_total'] = $nb_total;
383
384            if ($l == 0 || ($l > 0 && $l == $rs->level)) {
385                array_unshift($data, $t);
386            }
387        }
388
389        # We need to apply filter after counting
390        if (isset($params['cat_id']) && $params['cat_id'] !== '') {
391            $found = false;
392            foreach ($data as $v) {
393                if ($v['cat_id'] == $params['cat_id']) {
394                    $found = true;
395                    $data  = array($v);
396                    break;
397                }
398            }
399            if (!$found) {
400                $data = array();
401            }
402        }
403
404        if (isset($params['cat_url']) && ($params['cat_url'] !== '')
405            && !isset($params['cat_id'])) {
406            $found = false;
407            foreach ($data as $v) {
408                if ($v['cat_url'] == $params['cat_url']) {
409                    $found = true;
410                    $data  = array($v);
411                    break;
412                }
413            }
414            if (!$found) {
415                $data = array();
416            }
417        }
418
419        return staticRecord::newFromArray($data);
420    }
421
422    /**
423    Retrieves a category by its ID.
424
425    @param    id        <b>integer</b>        Category ID
426    @return    <b>record</b>
427     */
428    public function getCategory($id)
429    {
430        return $this->getCategories(array('cat_id' => $id));
431    }
432
433    /**
434    Retrieves parents of a given category.
435
436    @param    id        <b>integer</b>        Category ID
437    @return    <b>record</b>
438     */
439    public function getCategoryParents($id)
440    {
441        return $this->categories()->getParents($id);
442    }
443
444    /**
445    Retrieves first parent of a given category.
446
447    @param    id        <b>integer</b>        Category ID
448    @return    <b>record</b>
449     */
450    public function getCategoryParent($id)
451    {
452        return $this->categories()->getParent($id);
453    }
454
455    /**
456    Retrieves all category's first children
457
458    @param    id        <b>integer</b>        Category ID
459    @return    <b>record</b>
460     */
461    public function getCategoryFirstChildren($id)
462    {
463        return $this->getCategories(array('start' => $id, 'level' => $id == 0 ? 1 : 2));
464    }
465
466    private function getCategoriesCounter($params = array())
467    {
468        $strReq =
469        'SELECT  C.cat_id, COUNT(P.post_id) AS nb_post ' .
470        'FROM ' . $this->prefix . 'category AS C ' .
471        'JOIN ' . $this->prefix . "post P ON (C.cat_id = P.cat_id AND P.blog_id = '" . $this->con->escape($this->id) . "' ) " .
472        "WHERE C.blog_id = '" . $this->con->escape($this->id) . "' ";
473
474        if (!$this->core->auth->userID()) {
475            $strReq .= 'AND P.post_status = 1 ';
476        }
477
478        if (!empty($params['post_type'])) {
479            $strReq .= 'AND P.post_type ' . $this->con->in($params['post_type']);
480        }
481
482        $strReq .= 'GROUP BY C.cat_id ';
483
484        $rs       = $this->con->select($strReq);
485        $counters = array();
486        while ($rs->fetch()) {
487            $counters[$rs->cat_id] = $rs->nb_post;
488        }
489
490        return $counters;
491    }
492
493    /**
494    Creates a new category. Takes a cursor as input and returns the new category
495    ID.
496
497    @param    cur        <b>cursor</b>        Category cursor
498    @return    <b>integer</b>        New category ID
499     */
500    public function addCategory($cur, $parent = 0)
501    {
502        if (!$this->core->auth->check('categories', $this->id)) {
503            throw new Exception(__('You are not allowed to add categories'));
504        }
505
506        $url = array();
507        if ($parent != 0) {
508            $rs = $this->getCategory($parent);
509            if ($rs->isEmpty()) {
510                $url = array();
511            } else {
512                $url[] = $rs->cat_url;
513            }
514        }
515
516        if ($cur->cat_url == '') {
517            $url[] = text::tidyURL($cur->cat_title, false);
518        } else {
519            $url[] = $cur->cat_url;
520        }
521
522        $cur->cat_url = implode('/', $url);
523
524        $this->getCategoryCursor($cur);
525        $cur->blog_id = (string) $this->id;
526
527        # --BEHAVIOR-- coreBeforeCategoryCreate
528        $this->core->callBehavior('coreBeforeCategoryCreate', $this, $cur);
529
530        $id = $this->categories()->addNode($cur, $parent);
531        # Update category's cursor
532        $rs = $this->getCategory($id);
533        if (!$rs->isEmpty()) {
534            $cur->cat_lft = $rs->cat_lft;
535            $cur->cat_rgt = $rs->cat_rgt;
536        }
537
538        # --BEHAVIOR-- coreAfterCategoryCreate
539        $this->core->callBehavior('coreAfterCategoryCreate', $this, $cur);
540        $this->triggerBlog();
541
542        return $cur->cat_id;
543    }
544
545    /**
546    Updates an existing category.
547
548    @param    id        <b>integer</b>        Category ID
549    @param    cur        <b>cursor</b>        Category cursor
550     */
551    public function updCategory($id, $cur)
552    {
553        if (!$this->core->auth->check('categories', $this->id)) {
554            throw new Exception(__('You are not allowed to update categories'));
555        }
556
557        if ($cur->cat_url == '') {
558            $url = array();
559            $rs  = $this->categories()->getParents($id);
560            while ($rs->fetch()) {
561                if ($rs->index() == $rs->count() - 1) {
562                    $url[] = $rs->cat_url;
563                }
564            }
565
566            $url[]        = text::tidyURL($cur->cat_title, false);
567            $cur->cat_url = implode('/', $url);
568        }
569
570        $this->getCategoryCursor($cur, $id);
571
572        # --BEHAVIOR-- coreBeforeCategoryUpdate
573        $this->core->callBehavior('coreBeforeCategoryUpdate', $this, $cur);
574
575        $cur->update(
576            'WHERE cat_id = ' . (integer) $id . ' ' .
577            "AND blog_id = '" . $this->con->escape($this->id) . "' ");
578
579        # --BEHAVIOR-- coreAfterCategoryUpdate
580        $this->core->callBehavior('coreAfterCategoryUpdate', $this, $cur);
581
582        $this->triggerBlog();
583    }
584
585    /**
586    Set category position
587
588    @param  id              <b>integer</b>          Category ID
589    @param  left            <b>integer</b>          Category ID before
590    @param  right           <b>integer</b>          Category ID after
591     */
592    public function updCategoryPosition($id, $left, $right)
593    {
594        $this->categories()->updatePosition($id, $left, $right);
595        $this->triggerBlog();
596    }
597
598    /**
599    DEPRECATED METHOD. Use dcBlog::setCategoryParent and dcBlog::moveCategory
600    instead.
601
602    @param    id        <b>integer</b>        Category ID
603    @param    order    <b>integer</b>        Category position
604     */
605    public function updCategoryOrder($id, $order)
606    {
607        return;
608    }
609
610    /**
611    Set a category parent
612
613    @param    id        <b>integer</b>        Category ID
614    @param    parent    <b>integer</b>        Parent Category ID
615     */
616    public function setCategoryParent($id, $parent)
617    {
618        $this->categories()->setNodeParent($id, $parent);
619        $this->triggerBlog();
620    }
621
622    /**
623    Set category position
624
625    @param    id        <b>integer</b>        Category ID
626    @param    sibling    <b>integer</b>        Sibling Category ID
627    @param    move        <b>integer</b>        Order (before|after)
628     */
629    public function setCategoryPosition($id, $sibling, $move)
630    {
631        $this->categories()->setNodePosition($id, $sibling, $move);
632        $this->triggerBlog();
633    }
634
635    /**
636    Deletes a category.
637
638    @param    id        <b>integer</b>        Category ID
639     */
640    public function delCategory($id)
641    {
642        if (!$this->core->auth->check('categories', $this->id)) {
643            throw new Exception(__('You are not allowed to delete categories'));
644        }
645
646        $strReq = 'SELECT COUNT(post_id) AS nb_post ' .
647        'FROM ' . $this->prefix . 'post ' .
648        'WHERE cat_id = ' . (integer) $id . ' ' .
649        "AND blog_id = '" . $this->con->escape($this->id) . "' ";
650
651        $rs = $this->con->select($strReq);
652
653        if ($rs->nb_post > 0) {
654            throw new Exception(__('This category is not empty.'));
655        }
656
657        $this->categories()->deleteNode($id, true);
658        $this->triggerBlog();
659    }
660
661    /**
662    Reset categories order and relocate them to first level
663     */
664    public function resetCategoriesOrder()
665    {
666        if (!$this->core->auth->check('categories', $this->id)) {
667            throw new Exception(__('You are not allowed to reset categories order'));
668        }
669
670        $this->categories()->resetOrder();
671        $this->triggerBlog();
672    }
673
674    private function checkCategory($title, $url, $id = null)
675    {
676        # Let's check if URL is taken...
677        $strReq =
678        'SELECT cat_url FROM ' . $this->prefix . 'category ' .
679        "WHERE cat_url = '" . $this->con->escape($url) . "' " .
680        ($id ? 'AND cat_id <> ' . (integer) $id . ' ' : '') .
681        "AND blog_id = '" . $this->con->escape($this->id) . "' " .
682            'ORDER BY cat_url DESC';
683
684        $rs = $this->con->select($strReq);
685
686        if (!$rs->isEmpty()) {
687            if ($this->con->driver() == 'mysql' || $this->con->driver() == 'mysqli' || $this->con->driver() == 'mysqlimb4') {
688                $clause = "REGEXP '^" . $this->con->escape($url) . "[0-9]+$'";
689            } elseif ($this->con->driver() == 'pgsql') {
690                $clause = "~ '^" . $this->con->escape($url) . "[0-9]+$'";
691            } else {
692                $clause = "LIKE '" . $this->con->escape($url) . "%'";
693            }
694            $strReq =
695            'SELECT cat_url FROM ' . $this->prefix . 'category ' .
696            "WHERE cat_url " . $clause . ' ' .
697            ($id ? 'AND cat_id <> ' . (integer) $id . ' ' : '') .
698            "AND blog_id = '" . $this->con->escape($this->id) . "' " .
699                'ORDER BY cat_url DESC ';
700
701            $rs = $this->con->select($strReq);
702            $a  = array();
703            while ($rs->fetch()) {
704                $a[] = $rs->cat_url;
705            }
706
707            natsort($a);
708            $t_url = end($a);
709
710            if (preg_match('/(.*?)([0-9]+)$/', $t_url, $m)) {
711                $i   = (integer) $m[2];
712                $url = $m[1];
713            } else {
714                $i = 1;
715            }
716
717            return $url . ($i + 1);
718        }
719
720        # URL is empty?
721        if ($url == '') {
722            throw new Exception(__('Empty category URL'));
723        }
724
725        return $url;
726    }
727
728    private function getCategoryCursor($cur, $id = null)
729    {
730        if ($cur->cat_title == '') {
731            throw new Exception(__('You must provide a category title'));
732        }
733
734        # If we don't have any cat_url, let's do one
735        if ($cur->cat_url == '') {
736            $cur->cat_url = text::tidyURL($cur->cat_title, false);
737        }
738
739        # Still empty ?
740        if ($cur->cat_url == '') {
741            throw new Exception(__('You must provide a category URL'));
742        } else {
743            $cur->cat_url = text::tidyURL($cur->cat_url, true);
744        }
745
746        # Check if title or url are unique
747        $cur->cat_url = $this->checkCategory($cur->cat_title, $cur->cat_url, $id);
748
749        if ($cur->cat_desc !== null) {
750            $cur->cat_desc = $this->core->HTMLfilter($cur->cat_desc);
751        }
752    }
753    //@}
754
755    /// @name Entries management methods
756    //@{
757    /**
758    Retrieves entries. <b>$params</b> is an array taking the following
759    optionnal parameters:
760
761    - no_content: Don't retrieve entry content (excerpt and content)
762    - post_type: Get only entries with given type (default "post", array for many types and '' for no type)
763    - post_id: (integer or array) Get entry with given post_id
764    - post_url: Get entry with given post_url field
765    - user_id: (integer) Get entries belonging to given user ID
766    - cat_id: (string or array) Get entries belonging to given category ID
767    - cat_id_not: deprecated (use cat_id with "id ?not" instead)
768    - cat_url: (string or array) Get entries belonging to given category URL
769    - cat_url_not: deprecated (use cat_url with "url ?not" instead)
770    - post_status: (integer) Get entries with given post_status
771    - post_selected: (boolean) Get select flaged entries
772    - post_year: (integer) Get entries with given year
773    - post_month: (integer) Get entries with given month
774    - post_day: (integer) Get entries with given day
775    - post_lang: Get entries with given language code
776    - search: Get entries corresponding of the following search string
777    - columns: (array) More columns to retrieve
778    - sql: Append SQL string at the end of the query
779    - from: Append SQL string after "FROM" statement in query
780    - order: Order of results (default "ORDER BY post_dt DES")
781    - limit: Limit parameter
782    - sql_only : return the sql request instead of results. Only ids are selected
783    - exclude_post_id : (integer or array) Exclude entries with given post_id
784
785    Please note that on every cat_id or cat_url, you can add ?not to exclude
786    the category and ?sub to get subcategories.
787
788    @param    params        <b>array</b>        Parameters
789    @param    count_only    <b>boolean</b>        Only counts results
790    @return    <b>record</b>    A record with some more capabilities or the SQL request
791     */
792    public function getPosts($params = array(), $count_only = false)
793    {
794        # --BEHAVIOR-- coreBlogBeforeGetPosts
795        $params = new ArrayObject($params);
796        $this->core->callBehavior('coreBlogBeforeGetPosts', $params);
797
798        if ($count_only) {
799            $strReq = 'SELECT count(DISTINCT P.post_id) ';
800        } elseif (!empty($params['sql_only'])) {
801            $strReq = 'SELECT P.post_id ';
802        } else {
803            if (!empty($params['no_content'])) {
804                $content_req = '';
805            } else {
806                $content_req =
807                    'post_excerpt, post_excerpt_xhtml, ' .
808                    'post_content, post_content_xhtml, post_notes, ';
809            }
810
811            if (!empty($params['columns']) && is_array($params['columns'])) {
812                $content_req .= implode(', ', $params['columns']) . ', ';
813            }
814
815            $strReq =
816                'SELECT P.post_id, P.blog_id, P.user_id, P.cat_id, post_dt, ' .
817                'post_tz, post_creadt, post_upddt, post_format, post_password, ' .
818                'post_url, post_lang, post_title, ' . $content_req .
819                'post_type, post_meta, ' .
820                'post_status, post_firstpub, post_selected, post_position, ' .
821                'post_open_comment, post_open_tb, nb_comment, nb_trackback, ' .
822                'U.user_name, U.user_firstname, U.user_displayname, U.user_email, ' .
823                'U.user_url, ' .
824                'C.cat_title, C.cat_url, C.cat_desc ';
825        }
826
827        $strReq .=
828        'FROM ' . $this->prefix . 'post P ' .
829        'INNER JOIN ' . $this->prefix . 'user U ON U.user_id = P.user_id ' .
830        'LEFT OUTER JOIN ' . $this->prefix . 'category C ON P.cat_id = C.cat_id ';
831
832        if (!empty($params['from'])) {
833            $strReq .= $params['from'] . ' ';
834        }
835
836        $strReq .=
837        "WHERE P.blog_id = '" . $this->con->escape($this->id) . "' ";
838
839        if (!$this->core->auth->check('contentadmin', $this->id)) {
840            $strReq .= 'AND ((post_status = 1 ';
841
842            if ($this->without_password) {
843                $strReq .= 'AND post_password IS NULL ';
844            }
845            $strReq .= ') ';
846
847            if ($this->core->auth->userID()) {
848                $strReq .= "OR P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
849            } else {
850                $strReq .= ') ';
851            }
852        }
853
854        #Adding parameters
855        if (isset($params['post_type'])) {
856            if (is_array($params['post_type']) || $params['post_type'] != '') {
857                $strReq .= 'AND post_type ' . $this->con->in($params['post_type']);
858            }
859        } else {
860            $strReq .= "AND post_type = 'post' ";
861        }
862
863        if (isset($params['post_id']) && $params['post_id'] !== '') {
864            if (is_array($params['post_id'])) {
865                array_walk($params['post_id'], function (&$v, $k) {if ($v !== null) {$v = (integer) $v;}});
866            } else {
867                $params['post_id'] = array((integer) $params['post_id']);
868            }
869            $strReq .= 'AND P.post_id ' . $this->con->in($params['post_id']);
870        }
871
872        if (isset($params['exclude_post_id']) && $params['exclude_post_id'] !== '') {
873            if (is_array($params['exclude_post_id'])) {
874                array_walk($params['exclude_post_id'], function (&$v, $k) {if ($v !== null) {$v = (integer) $v;}});
875            } else {
876                $params['exclude_post_id'] = array((integer) $params['exclude_post_id']);
877            }
878            $strReq .= 'AND P.post_id NOT ' . $this->con->in($params['exclude_post_id']);
879        }
880
881        if (isset($params['post_url']) && $params['post_url'] !== '') {
882            $strReq .= "AND post_url = '" . $this->con->escape($params['post_url']) . "' ";
883        }
884
885        if (!empty($params['user_id'])) {
886            $strReq .= "AND U.user_id = '" . $this->con->escape($params['user_id']) . "' ";
887        }
888
889        if (isset($params['cat_id']) && $params['cat_id'] !== '') {
890            if (!is_array($params['cat_id'])) {
891                $params['cat_id'] = array($params['cat_id']);
892            }
893            if (!empty($params['cat_id_not'])) {
894                array_walk($params['cat_id'], function (&$v, $k) {$v = $v . " ?not";});
895            }
896            $strReq .= 'AND ' . $this->getPostsCategoryFilter($params['cat_id'], 'cat_id') . ' ';
897        } elseif (isset($params['cat_url']) && $params['cat_url'] !== '') {
898            if (!is_array($params['cat_url'])) {
899                $params['cat_url'] = array($params['cat_url']);
900            }
901            if (!empty($params['cat_url_not'])) {
902                array_walk($params['cat_url'], function (&$v, $k) {$v = $v . " ?not";});
903            }
904            $strReq .= 'AND ' . $this->getPostsCategoryFilter($params['cat_url'], 'cat_url') . ' ';
905        }
906
907        /* Other filters */
908        if (isset($params['post_status'])) {
909            $strReq .= 'AND post_status = ' . (integer) $params['post_status'] . ' ';
910        }
911
912        if (isset($params['post_firstpub'])) {
913            $strReq .= 'AND post_firstpub = ' . (integer) $params['post_firstpub'] . ' ';
914        }
915
916        if (isset($params['post_selected'])) {
917            $strReq .= 'AND post_selected = ' . (integer) $params['post_selected'] . ' ';
918        }
919
920        if (!empty($params['post_year'])) {
921            $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%Y') . ' = ' .
922            "'" . sprintf('%04d', $params['post_year']) . "' ";
923        }
924
925        if (!empty($params['post_month'])) {
926            $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%m') . ' = ' .
927            "'" . sprintf('%02d', $params['post_month']) . "' ";
928        }
929
930        if (!empty($params['post_day'])) {
931            $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%d') . ' = ' .
932            "'" . sprintf('%02d', $params['post_day']) . "' ";
933        }
934
935        if (!empty($params['post_lang'])) {
936            $strReq .= "AND P.post_lang = '" . $this->con->escape($params['post_lang']) . "' ";
937        }
938
939        if (!empty($params['search'])) {
940            $words = text::splitWords($params['search']);
941
942            if (!empty($words)) {
943                # --BEHAVIOR-- corePostSearch
944                if ($this->core->hasBehavior('corePostSearch')) {
945                    $this->core->callBehavior('corePostSearch', $this->core, array(&$words, &$strReq, &$params));
946                }
947
948                if ($words) {
949                    foreach ($words as $i => $w) {
950                        $words[$i] = "post_words LIKE '%" . $this->con->escape($w) . "%'";
951                    }
952                    $strReq .= 'AND ' . implode(' AND ', $words) . ' ';
953                }
954            }
955        }
956
957        if (isset($params['media'])) {
958            if ($params['media'] == '0') {
959                $strReq .= 'AND NOT ';
960            } else {
961                $strReq .= 'AND ';
962            }
963            $strReq .= 'EXISTS (SELECT M.post_id FROM ' . $this->prefix . 'post_media M ' .
964                'WHERE M.post_id = P.post_id ';
965            if (isset($params['link_type'])) {
966                $strReq .= " AND M.link_type " . $this->con->in($params['link_type']) . " ";
967            }
968            $strReq .= ")";
969        }
970
971        if (!empty($params['where'])) {
972            $strReq .= $params['where'] . ' ';
973        }
974
975        if (!empty($params['sql'])) {
976            $strReq .= $params['sql'] . ' ';
977        }
978
979        if (!$count_only) {
980            if (!empty($params['order'])) {
981                $strReq .= 'ORDER BY ' . $this->con->escape($params['order']) . ' ';
982            } else {
983                $strReq .= 'ORDER BY post_dt DESC ';
984            }
985        }
986
987        if (!$count_only && !empty($params['limit'])) {
988            $strReq .= $this->con->limit($params['limit']);
989        }
990
991        if (!empty($params['sql_only'])) {
992            return $strReq;
993        }
994
995        $rs            = $this->con->select($strReq);
996        $rs->core      = $this->core;
997        $rs->_nb_media = array();
998        $rs->extend('rsExtPost');
999
1000        # --BEHAVIOR-- coreBlogGetPosts
1001        $this->core->callBehavior('coreBlogGetPosts', $rs);
1002
1003        # --BEHAVIOR-- coreBlogAfterGetPosts
1004        $alt = new arrayObject(array('rs' => null, 'params' => $params, 'count_only' => $count_only));
1005        $this->core->callBehavior('coreBlogAfterGetPosts', $rs, $alt);
1006        if ($alt['rs'] instanceof record) {
1007            $rs = $alt['rs'];
1008        }
1009
1010        return $rs;
1011    }
1012
1013    /**
1014    Returns a record with post id, title and date for next or previous post
1015    according to the post ID.
1016    $dir could be 1 (next post) or -1 (previous post).
1017
1018    @param    post_id                <b>integer</b>        Post ID
1019    @param    dir                    <b>integer</b>        Search direction
1020    @param    restrict_to_category    <b>boolean</b>        Restrict to post with same category
1021    @param    restrict_to_lang        <b>boolean</b>        Restrict to post with same lang
1022    @return    record
1023     */
1024    public function getNextPost($post, $dir, $restrict_to_category = false, $restrict_to_lang = false)
1025    {
1026        $dt      = $post->post_dt;
1027        $post_id = (integer) $post->post_id;
1028
1029        if ($dir > 0) {
1030            $sign  = '>';
1031            $order = 'ASC';
1032        } else {
1033            $sign  = '<';
1034            $order = 'DESC';
1035        }
1036
1037        $params['post_type'] = $post->post_type;
1038        $params['limit']     = 1;
1039        $params['order']     = 'post_dt ' . $order . ', P.post_id ' . $order;
1040        $params['sql']       =
1041        'AND ( ' .
1042        "   (post_dt = '" . $this->con->escape($dt) . "' AND P.post_id " . $sign . " " . $post_id . ") " .
1043        "   OR post_dt " . $sign . " '" . $this->con->escape($dt) . "' " .
1044            ') ';
1045
1046        if ($restrict_to_category) {
1047            $params['sql'] .= $post->cat_id ? 'AND P.cat_id = ' . (integer) $post->cat_id . ' ' : 'AND P.cat_id IS NULL ';
1048        }
1049
1050        if ($restrict_to_lang) {
1051            $params['sql'] .= $post->post_lang ? 'AND P.post_lang = \'' . $this->con->escape($post->post_lang) . '\' ' : 'AND P.post_lang IS NULL ';
1052        }
1053
1054        $rs = $this->getPosts($params);
1055
1056        if ($rs->isEmpty()) {
1057            return;
1058        }
1059
1060        return $rs;
1061    }
1062
1063    /**
1064    Retrieves different languages and post count on blog, based on post_lang
1065    field. <var>$params</var> is an array taking the following optionnal
1066    parameters:
1067
1068    - post_type: Get only entries with given type (default "post", '' for no type)
1069    - lang: retrieve post count for selected lang
1070    - order: order statement (default post_lang DESC)
1071
1072    @param    params    <b>array</b>        Parameters
1073    @return    record
1074     */
1075    public function getLangs($params = array())
1076    {
1077        $strReq = 'SELECT COUNT(post_id) as nb_post, post_lang ' .
1078        'FROM ' . $this->prefix . 'post ' .
1079        "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
1080            "AND post_lang <> '' " .
1081            "AND post_lang IS NOT NULL ";
1082
1083        if (!$this->core->auth->check('contentadmin', $this->id)) {
1084            $strReq .= 'AND ((post_status = 1 ';
1085
1086            if ($this->without_password) {
1087                $strReq .= 'AND post_password IS NULL ';
1088            }
1089            $strReq .= ') ';
1090
1091            if ($this->core->auth->userID()) {
1092                $strReq .= "OR user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
1093            } else {
1094                $strReq .= ') ';
1095            }
1096        }
1097
1098        if (isset($params['post_type'])) {
1099            if ($params['post_type'] != '') {
1100                $strReq .= "AND post_type = '" . $this->con->escape($params['post_type']) . "' ";
1101            }
1102        } else {
1103            $strReq .= "AND post_type = 'post' ";
1104        }
1105
1106        if (isset($params['lang'])) {
1107            $strReq .= "AND post_lang = '" . $this->con->escape($params['lang']) . "' ";
1108        }
1109
1110        $strReq .= 'GROUP BY post_lang ';
1111
1112        $order = 'desc';
1113        if (!empty($params['order']) && preg_match('/^(desc|asc)$/i', $params['order'])) {
1114            $order = $params['order'];
1115        }
1116        $strReq .= 'ORDER BY post_lang ' . $order . ' ';
1117
1118        return $this->con->select($strReq);
1119    }
1120
1121    /**
1122    Returns a record with all distinct blog dates and post count.
1123    <var>$params</var> is an array taking the following optionnal parameters:
1124
1125    - type: (day|month|year) Get days, months or years
1126    - year: (integer) Get dates for given year
1127    - month: (integer) Get dates for given month
1128    - day: (integer) Get dates for given day
1129    - cat_id: (integer) Category ID filter
1130    - cat_url: Category URL filter
1131    - post_lang: lang of the posts
1132    - next: Get date following match
1133    - previous: Get date before match
1134    - order: Sort by date "ASC" or "DESC"
1135
1136    @param    params    <b>array</b>        Parameters array
1137    @return    record
1138     */
1139    public function getDates($params = array())
1140    {
1141        $dt_f  = '%Y-%m-%d';
1142        $dt_fc = '%Y%m%d';
1143        if (isset($params['type'])) {
1144            if ($params['type'] == 'year') {
1145                $dt_f  = '%Y-01-01';
1146                $dt_fc = '%Y0101';
1147            } elseif ($params['type'] == 'month') {
1148                $dt_f  = '%Y-%m-01';
1149                $dt_fc = '%Y%m01';
1150            }
1151        }
1152        $dt_f .= ' 00:00:00';
1153        $dt_fc .= '000000';
1154
1155        $cat_field = $catReq = $limit = '';
1156
1157        if (isset($params['cat_id']) && $params['cat_id'] !== '') {
1158            $catReq    = 'AND P.cat_id = ' . (integer) $params['cat_id'] . ' ';
1159            $cat_field = ', C.cat_url ';
1160        } elseif (isset($params['cat_url']) && $params['cat_url'] !== '') {
1161            $catReq    = "AND C.cat_url = '" . $this->con->escape($params['cat_url']) . "' ";
1162            $cat_field = ', C.cat_url ';
1163        }
1164        if (!empty($params['post_lang'])) {
1165            $catReq = 'AND P.post_lang = \'' . $params['post_lang'] . '\' ';
1166        }
1167
1168        $strReq = 'SELECT DISTINCT(' . $this->con->dateFormat('post_dt', $dt_f) . ') AS dt ' .
1169        $cat_field .
1170        ',COUNT(P.post_id) AS nb_post ' .
1171        'FROM ' . $this->prefix . 'post P LEFT JOIN ' . $this->prefix . 'category C ' .
1172        'ON P.cat_id = C.cat_id ' .
1173        "WHERE P.blog_id = '" . $this->con->escape($this->id) . "' " .
1174            $catReq;
1175
1176        if (!$this->core->auth->check('contentadmin', $this->id)) {
1177            $strReq .= 'AND ((post_status = 1 ';
1178
1179            if ($this->without_password) {
1180                $strReq .= 'AND post_password IS NULL ';
1181            }
1182            $strReq .= ') ';
1183
1184            if ($this->core->auth->userID()) {
1185                $strReq .= "OR P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
1186            } else {
1187                $strReq .= ') ';
1188            }
1189        }
1190
1191        if (!empty($params['post_type'])) {
1192            $strReq .= "AND post_type " . $this->con->in($params['post_type']) . " ";
1193        } else {
1194            $strReq .= "AND post_type = 'post' ";
1195        }
1196
1197        if (!empty($params['year'])) {
1198            $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%Y') . " = '" . sprintf('%04d', $params['year']) . "' ";
1199        }
1200
1201        if (!empty($params['month'])) {
1202            $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%m') . " = '" . sprintf('%02d', $params['month']) . "' ";
1203        }
1204
1205        if (!empty($params['day'])) {
1206            $strReq .= 'AND ' . $this->con->dateFormat('post_dt', '%d') . " = '" . sprintf('%02d', $params['day']) . "' ";
1207        }
1208
1209        # Get next or previous date
1210        if (!empty($params['next']) || !empty($params['previous'])) {
1211            if (!empty($params['next'])) {
1212                $pdir            = ' > ';
1213                $params['order'] = 'asc';
1214                $dt              = $params['next'];
1215            } else {
1216                $pdir            = ' < ';
1217                $params['order'] = 'desc';
1218                $dt              = $params['previous'];
1219            }
1220
1221            $dt = date('YmdHis', strtotime($dt));
1222
1223            $strReq .= 'AND ' . $this->con->dateFormat('post_dt', $dt_fc) . $pdir . "'" . $dt . "' ";
1224            $limit = $this->con->limit(1);
1225        }
1226
1227        $strReq .= 'GROUP BY dt ' . $cat_field;
1228
1229        $order = 'desc';
1230        if (!empty($params['order']) && preg_match('/^(desc|asc)$/i', $params['order'])) {
1231            $order = $params['order'];
1232        }
1233
1234        $strReq .=
1235            'ORDER BY dt ' . $order . ' ' .
1236            $limit;
1237
1238        $rs = $this->con->select($strReq);
1239        $rs->extend('rsExtDates');
1240        return $rs;
1241    }
1242
1243    /**
1244    Creates a new entry. Takes a cursor as input and returns the new entry
1245    ID.
1246
1247    @param    cur        <b>cursor</b>        Post cursor
1248    @return    <b>integer</b>        New post ID
1249     */
1250    public function addPost($cur)
1251    {
1252        if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
1253            throw new Exception(__('You are not allowed to create an entry'));
1254        }
1255
1256        $this->con->writeLock($this->prefix . 'post');
1257        try
1258        {
1259            # Get ID
1260            $rs = $this->con->select(
1261                'SELECT MAX(post_id) ' .
1262                'FROM ' . $this->prefix . 'post '
1263            );
1264
1265            $cur->post_id     = (integer) $rs->f(0) + 1;
1266            $cur->blog_id     = (string) $this->id;
1267            $cur->post_creadt = date('Y-m-d H:i:s');
1268            $cur->post_upddt  = date('Y-m-d H:i:s');
1269            $cur->post_tz     = $this->core->auth->getInfo('user_tz');
1270
1271            # Post excerpt and content
1272            $this->getPostContent($cur, $cur->post_id);
1273
1274            $this->getPostCursor($cur);
1275
1276            $cur->post_url = $this->getPostURL($cur->post_url, $cur->post_dt, $cur->post_title, $cur->post_id);
1277
1278            if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
1279                $cur->post_status = -2;
1280            }
1281
1282            # --BEHAVIOR-- coreBeforePostCreate
1283            $this->core->callBehavior('coreBeforePostCreate', $this, $cur);
1284
1285            $cur->insert();
1286            $this->con->unlock();
1287        } catch (Exception $e) {
1288            $this->con->unlock();
1289            throw $e;
1290        }
1291
1292        # --BEHAVIOR-- coreAfterPostCreate
1293        $this->core->callBehavior('coreAfterPostCreate', $this, $cur);
1294
1295        $this->triggerBlog();
1296
1297        $this->firstPublicationEntries($cur->post_id);
1298
1299        return $cur->post_id;
1300    }
1301
1302    /**
1303    Updates an existing post.
1304
1305    @param    id        <b>integer</b>        Post ID
1306    @param    cur        <b>cursor</b>        Post cursor
1307     */
1308    public function updPost($id, $cur)
1309    {
1310        if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
1311            throw new Exception(__('You are not allowed to update entries'));
1312        }
1313
1314        $id = (integer) $id;
1315
1316        if (empty($id)) {
1317            throw new Exception(__('No such entry ID'));
1318        }
1319
1320        # Post excerpt and content
1321        $this->getPostContent($cur, $id);
1322
1323        $this->getPostCursor($cur);
1324
1325        if ($cur->post_url !== null) {
1326            $cur->post_url = $this->getPostURL($cur->post_url, $cur->post_dt, $cur->post_title, $id);
1327        }
1328
1329        if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
1330            $cur->unsetField('post_status');
1331        }
1332
1333        $cur->post_upddt = date('Y-m-d H:i:s');
1334
1335        #If user is only "usage", we need to check the post's owner
1336        if (!$this->core->auth->check('contentadmin', $this->id)) {
1337            $strReq = 'SELECT post_id ' .
1338            'FROM ' . $this->prefix . 'post ' .
1339            'WHERE post_id = ' . $id . ' ' .
1340            "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
1341
1342            $rs = $this->con->select($strReq);
1343
1344            if ($rs->isEmpty()) {
1345                throw new Exception(__('You are not allowed to edit this entry'));
1346            }
1347        }
1348
1349        # --BEHAVIOR-- coreBeforePostUpdate
1350        $this->core->callBehavior('coreBeforePostUpdate', $this, $cur);
1351
1352        $cur->update('WHERE post_id = ' . $id . ' ');
1353
1354        # --BEHAVIOR-- coreAfterPostUpdate
1355        $this->core->callBehavior('coreAfterPostUpdate', $this, $cur);
1356
1357        $this->triggerBlog();
1358
1359        $this->firstPublicationEntries($id);
1360    }
1361
1362    /**
1363    Updates post status.
1364
1365    @param    id        <b>integer</b>        Post ID
1366    @param    status    <b>integer</b>        Post status
1367     */
1368    public function updPostStatus($id, $status)
1369    {
1370        $this->updPostsStatus($id, $status);
1371    }
1372
1373    /**
1374    Updates posts status.
1375
1376    @param    ids        <b>mixed</b>        Post(s) ID(s)
1377    @param    status    <b>integer</b>        Post status
1378     */
1379    public function updPostsStatus($ids, $status)
1380    {
1381        if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
1382            throw new Exception(__('You are not allowed to change this entry status'));
1383        }
1384
1385        $posts_ids = dcUtils::cleanIds($ids);
1386        $status    = (integer) $status;
1387
1388        $strReq = "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
1389        "AND post_id " . $this->con->in($posts_ids);
1390
1391        #If user can only publish, we need to check the post's owner
1392        if (!$this->core->auth->check('contentadmin', $this->id)) {
1393            $strReq .= "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
1394        }
1395
1396        $cur = $this->con->openCursor($this->prefix . 'post');
1397
1398        $cur->post_status = $status;
1399        $cur->post_upddt  = date('Y-m-d H:i:s');
1400
1401        $cur->update($strReq);
1402        $this->triggerBlog();
1403
1404        $this->firstPublicationEntries($posts_ids);
1405    }
1406
1407    /**
1408    Updates post selection.
1409
1410    @param    id        <b>integer</b>        Post ID
1411    @param    selected    <b>integer</b>        Is selected post
1412     */
1413    public function updPostSelected($id, $selected)
1414    {
1415        $this->updPostsSelected($id, $selected);
1416    }
1417
1418    /**
1419    Updates posts selection.
1420
1421    @param    ids        <b>mixed</b>        Post(s) ID(s)
1422    @param    selected    <b>integer</b>        Is selected post(s)
1423     */
1424    public function updPostsSelected($ids, $selected)
1425    {
1426        if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
1427            throw new Exception(__('You are not allowed to change this entry category'));
1428        }
1429
1430        $posts_ids = dcUtils::cleanIds($ids);
1431        $selected  = (boolean) $selected;
1432
1433        $strReq = "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
1434        "AND post_id " . $this->con->in($posts_ids);
1435
1436        # If user is only usage, we need to check the post's owner
1437        if (!$this->core->auth->check('contentadmin', $this->id)) {
1438            $strReq .= "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
1439        }
1440
1441        $cur = $this->con->openCursor($this->prefix . 'post');
1442
1443        $cur->post_selected = (integer) $selected;
1444        $cur->post_upddt    = date('Y-m-d H:i:s');
1445
1446        $cur->update($strReq);
1447        $this->triggerBlog();
1448    }
1449
1450    /**
1451    Updates post category. <var>$cat_id</var> can be null.
1452
1453    @param    id        <b>integer</b>        Post ID
1454    @param    cat_id    <b>integer</b>        Category ID
1455     */
1456    public function updPostCategory($id, $cat_id)
1457    {
1458        $this->updPostsCategory($id, $cat_id);
1459    }
1460
1461    /**
1462    Updates posts category. <var>$cat_id</var> can be null.
1463
1464    @param    ids        <b>mixed</b>        Post(s) ID(s)
1465    @param    cat_id    <b>integer</b>        Category ID
1466     */
1467    public function updPostsCategory($ids, $cat_id)
1468    {
1469        if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
1470            throw new Exception(__('You are not allowed to change this entry category'));
1471        }
1472
1473        $posts_ids = dcUtils::cleanIds($ids);
1474        $cat_id    = (integer) $cat_id;
1475
1476        $strReq = "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
1477        "AND post_id " . $this->con->in($posts_ids);
1478
1479        # If user is only usage, we need to check the post's owner
1480        if (!$this->core->auth->check('contentadmin', $this->id)) {
1481            $strReq .= "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
1482        }
1483
1484        $cur = $this->con->openCursor($this->prefix . 'post');
1485
1486        $cur->cat_id     = ($cat_id ?: null);
1487        $cur->post_upddt = date('Y-m-d H:i:s');
1488
1489        $cur->update($strReq);
1490        $this->triggerBlog();
1491    }
1492
1493    /**
1494    Updates posts category. <var>$new_cat_id</var> can be null.
1495
1496    @param    old_cat_id    <b>integer</b>        Old category ID
1497    @param    new_cat_id    <b>integer</b>        New category ID
1498     */
1499    public function changePostsCategory($old_cat_id, $new_cat_id)
1500    {
1501        if (!$this->core->auth->check('contentadmin,categories', $this->id)) {
1502            throw new Exception(__('You are not allowed to change entries category'));
1503        }
1504
1505        $old_cat_id = (integer) $old_cat_id;
1506        $new_cat_id = (integer) $new_cat_id;
1507
1508        $cur = $this->con->openCursor($this->prefix . 'post');
1509
1510        $cur->cat_id     = ($new_cat_id ?: null);
1511        $cur->post_upddt = date('Y-m-d H:i:s');
1512
1513        $cur->update(
1514            'WHERE cat_id = ' . $old_cat_id . ' ' .
1515            "AND blog_id = '" . $this->con->escape($this->id) . "' "
1516        );
1517        $this->triggerBlog();
1518    }
1519
1520    /**
1521    Deletes a post.
1522
1523    @param    id        <b>integer</b>        Post ID
1524     */
1525    public function delPost($id)
1526    {
1527        $this->delPosts($id);
1528    }
1529
1530    /**
1531    Deletes multiple posts.
1532
1533    @param    ids        <b>mixed</b>        Post(s) ID(s)
1534     */
1535    public function delPosts($ids)
1536    {
1537        if (!$this->core->auth->check('delete,contentadmin', $this->id)) {
1538            throw new Exception(__('You are not allowed to delete entries'));
1539        }
1540
1541        $posts_ids = dcUtils::cleanIds($ids);
1542
1543        if (empty($posts_ids)) {
1544            throw new Exception(__('No such entry ID'));
1545        }
1546
1547        $strReq = 'DELETE FROM ' . $this->prefix . 'post ' .
1548        "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
1549        "AND post_id " . $this->con->in($posts_ids);
1550
1551        #If user can only delete, we need to check the post's owner
1552        if (!$this->core->auth->check('contentadmin', $this->id)) {
1553            $strReq .= "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
1554        }
1555
1556        $this->con->execute($strReq);
1557        $this->triggerBlog();
1558    }
1559
1560    /**
1561    Publishes all entries flaged as "scheduled".
1562     */
1563    public function publishScheduledEntries()
1564    {
1565        $strReq = 'SELECT post_id, post_dt, post_tz ' .
1566        'FROM ' . $this->prefix . 'post ' .
1567        'WHERE post_status = -1 ' .
1568        "AND blog_id = '" . $this->con->escape($this->id) . "' ";
1569
1570        $rs = $this->con->select($strReq);
1571
1572        $now       = dt::toUTC(time());
1573        $to_change = new ArrayObject();
1574
1575        if ($rs->isEmpty()) {
1576            return;
1577        }
1578
1579        while ($rs->fetch()) {
1580            # Now timestamp with post timezone
1581            $now_tz = $now + dt::getTimeOffset($rs->post_tz, $now);
1582
1583            # Post timestamp
1584            $post_ts = strtotime($rs->post_dt);
1585
1586            # If now_tz >= post_ts, we publish the entry
1587            if ($now_tz >= $post_ts) {
1588                $to_change[] = (integer) $rs->post_id;
1589            }
1590        }
1591        if (count($to_change)) {
1592            # --BEHAVIOR-- coreBeforeScheduledEntriesPublish
1593            $this->core->callBehavior('coreBeforeScheduledEntriesPublish', $this, $to_change);
1594
1595            $strReq =
1596            'UPDATE ' . $this->prefix . 'post SET ' .
1597            'post_status = 1 ' .
1598            "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
1599            'AND post_id ' . $this->con->in((array) $to_change) . ' ';
1600            $this->con->execute($strReq);
1601            $this->triggerBlog();
1602
1603            # --BEHAVIOR-- coreAfterScheduledEntriesPublish
1604            $this->core->callBehavior('coreAfterScheduledEntriesPublish', $this, $to_change);
1605
1606            $this->firstPublicationEntries($to_change);
1607        }
1608    }
1609
1610    /**
1611    First publication mecanism (on post create, update, publish, status)
1612
1613    @param    ids        <b>mixed</b>        Post(s) ID(s)
1614     */
1615    public function firstPublicationEntries($ids)
1616    {
1617        $posts = $this->getPosts(array(
1618            'post_id'       => dcUtils::cleanIds($ids),
1619            'post_status'   => 1,
1620            'post_firstpub' => 0
1621        ));
1622
1623        $to_change = array();
1624        while ($posts->fetch()) {
1625
1626            $to_change[] = $posts->post_id;
1627        }
1628
1629        if (count($to_change)) {
1630
1631            $strReq =
1632            'UPDATE ' . $this->prefix . 'post ' .
1633            'SET post_firstpub = 1 ' .
1634            "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
1635            'AND post_id ' . $this->con->in((array) $to_change) . ' ';
1636            $this->con->execute($strReq);
1637
1638            # --BEHAVIOR-- coreFirstPublicationEntries
1639            $this->core->callBehavior('coreFirstPublicationEntries', $this, $to_change);
1640        }
1641    }
1642
1643    /**
1644    Retrieves all users having posts on current blog.
1645
1646    @param    post_type        <b>string</b>        post_type filter (post)
1647    @return    record
1648     */
1649    public function getPostsUsers($post_type = 'post')
1650    {
1651        $strReq = 'SELECT P.user_id, user_name, user_firstname, ' .
1652        'user_displayname, user_email ' .
1653        'FROM ' . $this->prefix . 'post P, ' . $this->prefix . 'user U ' .
1654        'WHERE P.user_id = U.user_id ' .
1655        "AND blog_id = '" . $this->con->escape($this->id) . "' ";
1656
1657        if ($post_type) {
1658            $strReq .= "AND post_type = '" . $this->con->escape($post_type) . "' ";
1659        }
1660
1661        $strReq .= 'GROUP BY P.user_id, user_name, user_firstname, user_displayname, user_email ';
1662
1663        return $this->con->select($strReq);
1664    }
1665
1666    private function getPostsCategoryFilter($arr, $field = 'cat_id')
1667    {
1668        $field = $field == 'cat_id' ? 'cat_id' : 'cat_url';
1669
1670        $sub     = array();
1671        $not     = array();
1672        $queries = array();
1673
1674        foreach ($arr as $v) {
1675            $v    = trim($v);
1676            $args = preg_split('/\s*[?]\s*/', $v, -1, PREG_SPLIT_NO_EMPTY);
1677            $id   = array_shift($args);
1678            $args = array_flip($args);
1679
1680            if (isset($args['not'])) {$not[$id] = 1;}
1681            if (isset($args['sub'])) {$sub[$id] = 1;}
1682            if ($field == 'cat_id') {
1683                if (preg_match('/^null$/i', $id)) {
1684                    $queries[$id] = 'P.cat_id IS NULL';
1685                } else {
1686                    $queries[$id] = 'P.cat_id = ' . (integer) $id;
1687                }
1688            } else {
1689                $queries[$id] = "C.cat_url = '" . $this->con->escape($id) . "' ";
1690            }
1691        }
1692
1693        if (!empty($sub)) {
1694            $rs = $this->con->select(
1695                'SELECT cat_id, cat_url, cat_lft, cat_rgt FROM ' . $this->prefix . 'category ' .
1696                "WHERE blog_id = '" . $this->con->escape($this->id) . "' " .
1697                'AND ' . $field . ' ' . $this->con->in(array_keys($sub))
1698            );
1699
1700            while ($rs->fetch()) {
1701                $queries[$rs->f($field)] = '(C.cat_lft BETWEEN ' . $rs->cat_lft . ' AND ' . $rs->cat_rgt . ')';
1702            }
1703        }
1704
1705        # Create queries
1706        $sql = array(
1707            0 => array(), # wanted categories
1708            1 => array() # excluded categories
1709        );
1710
1711        foreach ($queries as $id => $q) {
1712            $sql[(integer) isset($not[$id])][] = $q;
1713        }
1714
1715        $sql[0] = implode(' OR ', $sql[0]);
1716        $sql[1] = implode(' OR ', $sql[1]);
1717
1718        if ($sql[0]) {
1719            $sql[0] = '(' . $sql[0] . ')';
1720        } else {
1721            unset($sql[0]);
1722        }
1723
1724        if ($sql[1]) {
1725            $sql[1] = '(P.cat_id IS NULL OR NOT(' . $sql[1] . '))';
1726        } else {
1727            unset($sql[1]);
1728        }
1729
1730        return implode(' AND ', $sql);
1731    }
1732
1733    private function getPostCursor($cur, $post_id = null)
1734    {
1735        if ($cur->post_title == '') {
1736            throw new Exception(__('No entry title'));
1737        }
1738
1739        if ($cur->post_content == '') {
1740            throw new Exception(__('No entry content'));
1741        }
1742
1743        if ($cur->post_password === '') {
1744            $cur->post_password = null;
1745        }
1746
1747        if ($cur->post_dt == '') {
1748            $offset       = dt::getTimeOffset($this->core->auth->getInfo('user_tz'));
1749            $now          = time() + $offset;
1750            $cur->post_dt = date('Y-m-d H:i:00', $now);
1751        }
1752
1753        $post_id = is_int($post_id) ? $post_id : $cur->post_id;
1754
1755        if ($cur->post_content_xhtml == '') {
1756            throw new Exception(__('No entry content'));
1757        }
1758
1759        # Words list
1760        if ($cur->post_title !== null && $cur->post_excerpt_xhtml !== null
1761            && $cur->post_content_xhtml !== null) {
1762            $words =
1763            $cur->post_title . ' ' .
1764            $cur->post_excerpt_xhtml . ' ' .
1765            $cur->post_content_xhtml;
1766
1767            $cur->post_words = implode(' ', text::splitWords($words));
1768        }
1769
1770        if ($cur->isField('post_firstpub')) {
1771            $cur->unsetField('post_firstpub');
1772        }
1773    }
1774
1775    private function getPostContent($cur, $post_id)
1776    {
1777        $post_excerpt       = $cur->post_excerpt;
1778        $post_excerpt_xhtml = $cur->post_excerpt_xhtml;
1779        $post_content       = $cur->post_content;
1780        $post_content_xhtml = $cur->post_content_xhtml;
1781
1782        $this->setPostContent(
1783            $post_id, $cur->post_format, $cur->post_lang,
1784            $post_excerpt, $post_excerpt_xhtml,
1785            $post_content, $post_content_xhtml
1786        );
1787
1788        $cur->post_excerpt       = $post_excerpt;
1789        $cur->post_excerpt_xhtml = $post_excerpt_xhtml;
1790        $cur->post_content       = $post_content;
1791        $cur->post_content_xhtml = $post_content_xhtml;
1792    }
1793
1794    /**
1795    Creates post HTML content, taking format and lang into account.
1796
1797    @param        post_id        <b>integer</b>        Post ID
1798    @param        format        <b>string</b>        Post format
1799    @param        lang            <b>string</b>        Post lang
1800    @param        excerpt        <b>string</b>        Post excerpt
1801    @param[out]    excerpt_xhtml    <b>string</b>        Post excerpt HTML
1802    @param        content        <b>string</b>        Post content
1803    @param[out]    content_xhtml    <b>string</b>        Post content HTML
1804     */
1805    public function setPostContent($post_id, $format, $lang, &$excerpt, &$excerpt_xhtml, &$content, &$content_xhtml)
1806    {
1807        if ($format == 'wiki') {
1808            $this->core->initWikiPost();
1809            $this->core->wiki2xhtml->setOpt('note_prefix', 'pnote-' . $post_id);
1810            switch ($this->settings->system->note_title_tag) {
1811                case 1:
1812                    $tag = 'h3';
1813                    break;
1814                case 2:
1815                    $tag = 'p';
1816                    break;
1817                default:
1818                    $tag = 'h4';
1819                    break;
1820            }
1821            $this->core->wiki2xhtml->setOpt('note_str', '<div class="footnotes"><' . $tag . ' class="footnotes-title">' .
1822                __('Notes') . '</' . $tag . '>%s</div>');
1823            $this->core->wiki2xhtml->setOpt('note_str_single', '<div class="footnotes"><' . $tag . ' class="footnotes-title">' .
1824                __('Note') . '</' . $tag . '>%s</div>');
1825            if (strpos($lang, 'fr') === 0) {
1826                $this->core->wiki2xhtml->setOpt('active_fr_syntax', 1);
1827            }
1828        }
1829
1830        if ($excerpt) {
1831            $excerpt_xhtml = $this->core->callFormater($format, $excerpt);
1832            $excerpt_xhtml = $this->core->HTMLfilter($excerpt_xhtml);
1833        } else {
1834            $excerpt_xhtml = '';
1835        }
1836
1837        if ($content) {
1838            $content_xhtml = $this->core->callFormater($format, $content);
1839            $content_xhtml = $this->core->HTMLfilter($content_xhtml);
1840        } else {
1841            $content_xhtml = '';
1842        }
1843
1844        # --BEHAVIOR-- coreAfterPostContentFormat
1845        $this->core->callBehavior('coreAfterPostContentFormat', array(
1846            'excerpt'       => &$excerpt,
1847            'content'       => &$content,
1848            'excerpt_xhtml' => &$excerpt_xhtml,
1849            'content_xhtml' => &$content_xhtml
1850        ));
1851    }
1852
1853    /**
1854    Returns URL for a post according to blog setting <var>post_url_format</var>.
1855    It will try to guess URL and append some figures if needed.
1856
1857    @param    url            <b>string</b>        Origin URL, could be empty
1858    @param    post_dt        <b>string</b>        Post date (in YYYY-MM-DD HH:mm:ss)
1859    @param    post_title    <b>string</b>        Post title
1860    @param    post_id        <b>integer</b>        Post ID
1861    @return    <b>string</b>    result URL
1862     */
1863    public function getPostURL($url, $post_dt, $post_title, $post_id)
1864    {
1865        $url = trim($url);
1866
1867        $url_patterns = array(
1868            '{y}'  => date('Y', strtotime($post_dt)),
1869            '{m}'  => date('m', strtotime($post_dt)),
1870            '{d}'  => date('d', strtotime($post_dt)),
1871            '{t}'  => text::tidyURL($post_title),
1872            '{id}' => (integer) $post_id
1873        );
1874
1875        # If URL is empty, we create a new one
1876        if ($url == '') {
1877            # Transform with format
1878            $url = str_replace(
1879                array_keys($url_patterns),
1880                array_values($url_patterns),
1881                $this->settings->system->post_url_format
1882            );
1883        } else {
1884            $url = text::tidyURL($url);
1885        }
1886
1887        # Let's check if URL is taken...
1888        $strReq = 'SELECT post_url FROM ' . $this->prefix . 'post ' .
1889        "WHERE post_url = '" . $this->con->escape($url) . "' " .
1890        'AND post_id <> ' . (integer) $post_id . ' ' .
1891        "AND blog_id = '" . $this->con->escape($this->id) . "' " .
1892            'ORDER BY post_url DESC';
1893
1894        $rs = $this->con->select($strReq);
1895
1896        if (!$rs->isEmpty()) {
1897            if ($this->con->driver() == 'mysql' || $this->con->driver() == 'mysqli' || $this->con->driver() == 'mysqlimb4') {
1898                $clause = "REGEXP '^" . $this->con->escape(preg_quote($url)) . "[0-9]+$'";
1899            } elseif ($this->con->driver() == 'pgsql') {
1900                $clause = "~ '^" . $this->con->escape(preg_quote($url)) . "[0-9]+$'";
1901            } else {
1902                $clause = "LIKE '" .
1903                $this->con->escape(preg_replace(array('%', '_', '!'), array('!%', '!_', '!!'), $url)) .
1904                    "%' ESCAPE '!'";
1905            }
1906            $strReq = 'SELECT post_url FROM ' . $this->prefix . 'post ' .
1907            "WHERE post_url " . $clause . ' ' .
1908            'AND post_id <> ' . (integer) $post_id . ' ' .
1909            "AND blog_id = '" . $this->con->escape($this->id) . "' " .
1910                'ORDER BY post_url DESC ';
1911
1912            $rs = $this->con->select($strReq);
1913            $a  = array();
1914            while ($rs->fetch()) {
1915                $a[] = $rs->post_url;
1916            }
1917
1918            natsort($a);
1919            $t_url = end($a);
1920
1921            if (preg_match('/(.*?)([0-9]+)$/', $t_url, $m)) {
1922                $i   = (integer) $m[2];
1923                $url = $m[1];
1924            } else {
1925                $i = 1;
1926            }
1927
1928            return $url . ($i + 1);
1929        }
1930
1931        # URL is empty?
1932        if ($url == '') {
1933            throw new Exception(__('Empty entry URL'));
1934        }
1935
1936        return $url;
1937    }
1938    //@}
1939
1940    /// @name Comments management methods
1941    //@{
1942    /**
1943    Retrieves comments. <b>$params</b> is an array taking the following
1944    optionnal parameters:
1945
1946    - no_content: Don't retrieve comment content
1947    - post_type: Get only entries with given type (default no type, array for many types)
1948    - post_id: (integer) Get comments belonging to given post_id
1949    - cat_id: (integer or array) Get comments belonging to entries of given category ID
1950    - comment_id: (integer or array) Get comment with given ID (or IDs)
1951    - comment_site: (string) Get comments with given comment_site
1952    - comment_status: (integer) Get comments with given comment_status
1953    - comment_trackback: (integer) Get only comments (0) or trackbacks (1)
1954    - comment_ip: (string) Get comments with given IP address
1955    - post_url: Get entry with given post_url field
1956    - user_id: (integer) Get entries belonging to given user ID
1957    - q_author: Search comments by author
1958    - sql: Append SQL string at the end of the query
1959    - from: Append SQL string after "FROM" statement in query
1960    - order: Order of results (default "ORDER BY comment_dt DES")
1961    - limit: Limit parameter
1962    - sql_only : return the sql request instead of results. Only ids are selected
1963
1964    @param    params        <b>array</b>        Parameters
1965    @param    count_only    <b>boolean</b>        Only counts results
1966    @return    <b>record</b>    A record with some more capabilities
1967     */
1968    public function getComments($params = array(), $count_only = false)
1969    {
1970        if ($count_only) {
1971            $strReq = 'SELECT count(comment_id) ';
1972        } elseif (!empty($params['sql_only'])) {
1973            $strReq = 'SELECT P.post_id ';
1974        } else {
1975            if (!empty($params['no_content'])) {
1976                $content_req = '';
1977            } else {
1978                $content_req = 'comment_content, ';
1979            }
1980
1981            if (!empty($params['columns']) && is_array($params['columns'])) {
1982                $content_req .= implode(', ', $params['columns']) . ', ';
1983            }
1984
1985            $strReq =
1986                'SELECT C.comment_id, comment_dt, comment_tz, comment_upddt, ' .
1987                'comment_author, comment_email, comment_site, ' .
1988                $content_req . ' comment_trackback, comment_status, ' .
1989                'comment_spam_status, comment_spam_filter, comment_ip, ' .
1990                'P.post_title, P.post_url, P.post_id, P.post_password, P.post_type, ' .
1991                'P.post_dt, P.user_id, U.user_email, U.user_url ';
1992        }
1993
1994        $strReq .=
1995        'FROM ' . $this->prefix . 'comment C ' .
1996        'INNER JOIN ' . $this->prefix . 'post P ON C.post_id = P.post_id ' .
1997        'INNER JOIN ' . $this->prefix . 'user U ON P.user_id = U.user_id ';
1998
1999        if (!empty($params['from'])) {
2000            $strReq .= $params['from'] . ' ';
2001        }
2002
2003        $strReq .=
2004        "WHERE P.blog_id = '" . $this->con->escape($this->id) . "' ";
2005
2006        if (!$this->core->auth->check('contentadmin', $this->id)) {
2007            $strReq .= 'AND ((comment_status = 1 AND P.post_status = 1 ';
2008
2009            if ($this->without_password) {
2010                $strReq .= 'AND post_password IS NULL ';
2011            }
2012            $strReq .= ') ';
2013
2014            if ($this->core->auth->userID()) {
2015                $strReq .= "OR P.user_id = '" . $this->con->escape($this->core->auth->userID()) . "')";
2016            } else {
2017                $strReq .= ') ';
2018            }
2019        }
2020
2021        if (!empty($params['post_type'])) {
2022            $strReq .= 'AND post_type ' . $this->con->in($params['post_type']);
2023        }
2024
2025        if (isset($params['post_id']) && $params['post_id'] !== '') {
2026            $strReq .= 'AND P.post_id = ' . (integer) $params['post_id'] . ' ';
2027        }
2028
2029        if (isset($params['cat_id']) && $params['cat_id'] !== '') {
2030            $strReq .= 'AND P.cat_id = ' . (integer) $params['cat_id'] . ' ';
2031        }
2032
2033        if (isset($params['comment_id']) && $params['comment_id'] !== '') {
2034            if (is_array($params['comment_id'])) {
2035                array_walk($params['comment_id'], function (&$v, $k) {if ($v !== null) {$v = (integer) $v;}});
2036            } else {
2037                $params['comment_id'] = array((integer) $params['comment_id']);
2038            }
2039            $strReq .= 'AND comment_id ' . $this->con->in($params['comment_id']);
2040        }
2041
2042        if (isset($params['comment_email'])) {
2043            $comment_email = $this->con->escape(str_replace('*', '%', $params['comment_email']));
2044            $strReq .= "AND comment_email LIKE '" . $comment_email . "' ";
2045        }
2046
2047        if (isset($params['comment_site'])) {
2048            $comment_site = $this->con->escape(str_replace('*', '%', $params['comment_site']));
2049            $strReq .= "AND comment_site LIKE '" . $comment_site . "' ";
2050        }
2051
2052        if (isset($params['comment_status'])) {
2053            $strReq .= 'AND comment_status = ' . (integer) $params['comment_status'] . ' ';
2054        }
2055
2056        if (!empty($params['comment_status_not'])) {
2057            $strReq .= 'AND comment_status <> ' . (integer) $params['comment_status_not'] . ' ';
2058        }
2059
2060        if (isset($params['comment_trackback'])) {
2061            $strReq .= 'AND comment_trackback = ' . (integer) (boolean) $params['comment_trackback'] . ' ';
2062        }
2063
2064        if (isset($params['comment_ip'])) {
2065            $comment_ip = $this->con->escape(str_replace('*', '%', $params['comment_ip']));
2066            $strReq .= "AND comment_ip LIKE '" . $comment_ip . "' ";
2067        }
2068
2069        if (isset($params['q_author'])) {
2070            $q_author = $this->con->escape(str_replace('*', '%', strtolower($params['q_author'])));
2071            $strReq .= "AND LOWER(comment_author) LIKE '" . $q_author . "' ";
2072        }
2073
2074        if (!empty($params['search'])) {
2075            $words = text::splitWords($params['search']);
2076
2077            if (!empty($words)) {
2078                # --BEHAVIOR coreCommentSearch
2079                if ($this->core->hasBehavior('coreCommentSearch')) {
2080                    $this->core->callBehavior('coreCommentSearch', $this->core, array(&$words, &$strReq, &$params));
2081                }
2082
2083                if ($words) {
2084                    foreach ($words as $i => $w) {
2085                        $words[$i] = "comment_words LIKE '%" . $this->con->escape($w) . "%'";
2086                    }
2087                    $strReq .= 'AND ' . implode(' AND ', $words) . ' ';
2088                }
2089            }
2090        }
2091
2092        if (!empty($params['sql'])) {
2093            $strReq .= $params['sql'] . ' ';
2094        }
2095
2096        if (!$count_only) {
2097            if (!empty($params['order'])) {
2098                $strReq .= 'ORDER BY ' . $this->con->escape($params['order']) . ' ';
2099            } else {
2100                $strReq .= 'ORDER BY comment_dt DESC ';
2101            }
2102        }
2103
2104        if (!$count_only && !empty($params['limit'])) {
2105            $strReq .= $this->con->limit($params['limit']);
2106        }
2107
2108        if (!empty($params['sql_only'])) {
2109            return $strReq;
2110        }
2111
2112        $rs       = $this->con->select($strReq);
2113        $rs->core = $this->core;
2114        $rs->extend('rsExtComment');
2115
2116        # --BEHAVIOR-- coreBlogGetComments
2117        $this->core->callBehavior('coreBlogGetComments', $rs);
2118
2119        return $rs;
2120    }
2121
2122    /**
2123    Creates a new comment. Takes a cursor as input and returns the new comment
2124    ID.
2125
2126    @param    cur        <b>cursor</b>        Comment cursor
2127    @return    <b>integer</b>        New comment ID
2128     */
2129    public function addComment($cur)
2130    {
2131        $this->con->writeLock($this->prefix . 'comment');
2132        try
2133        {
2134            # Get ID
2135            $rs = $this->con->select(
2136                'SELECT MAX(comment_id) ' .
2137                'FROM ' . $this->prefix . 'comment '
2138            );
2139
2140            $cur->comment_id    = (integer) $rs->f(0) + 1;
2141            $cur->comment_upddt = date('Y-m-d H:i:s');
2142
2143            $offset          = dt::getTimeOffset($this->settings->system->blog_timezone);
2144            $cur->comment_dt = date('Y-m-d H:i:s', time() + $offset);
2145            $cur->comment_tz = $this->settings->system->blog_timezone;
2146
2147            $this->getCommentCursor($cur);
2148
2149            if ($cur->comment_ip === null) {
2150                $cur->comment_ip = http::realIP();
2151            }
2152
2153            # --BEHAVIOR-- coreBeforeCommentCreate
2154            $this->core->callBehavior('coreBeforeCommentCreate', $this, $cur);
2155
2156            $cur->insert();
2157            $this->con->unlock();
2158        } catch (Exception $e) {
2159            $this->con->unlock();
2160            throw $e;
2161        }
2162
2163        # --BEHAVIOR-- coreAfterCommentCreate
2164        $this->core->callBehavior('coreAfterCommentCreate', $this, $cur);
2165
2166        $this->triggerComment($cur->comment_id);
2167        if ($cur->comment_status != -2) {
2168            $this->triggerBlog();
2169        }
2170        return $cur->comment_id;
2171    }
2172
2173    /**
2174    Updates an existing comment.
2175
2176    @param    id        <b>integer</b>        Comment ID
2177    @param    cur        <b>cursor</b>        Comment cursor
2178     */
2179    public function updComment($id, $cur)
2180    {
2181        if (!$this->core->auth->check('usage,contentadmin', $this->id)) {
2182            throw new Exception(__('You are not allowed to update comments'));
2183        }
2184
2185        $id = (integer) $id;
2186
2187        if (empty($id)) {
2188            throw new Exception(__('No such comment ID'));
2189        }
2190
2191        $rs = $this->getComments(array('comment_id' => $id));
2192
2193        if ($rs->isEmpty()) {
2194            throw new Exception(__('No such comment ID'));
2195        }
2196
2197        #If user is only usage, we need to check the post's owner
2198        if (!$this->core->auth->check('contentadmin', $this->id)) {
2199            if ($rs->user_id != $this->core->auth->userID()) {
2200                throw new Exception(__('You are not allowed to update this comment'));
2201            }
2202        }
2203
2204        $this->getCommentCursor($cur);
2205
2206        $cur->comment_upddt = date('Y-m-d H:i:s');
2207
2208        if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
2209            $cur->unsetField('comment_status');
2210        }
2211
2212        # --BEHAVIOR-- coreBeforeCommentUpdate
2213        $this->core->callBehavior('coreBeforeCommentUpdate', $this, $cur, $rs);
2214
2215        $cur->update('WHERE comment_id = ' . $id . ' ');
2216
2217        # --BEHAVIOR-- coreAfterCommentUpdate
2218        $this->core->callBehavior('coreAfterCommentUpdate', $this, $cur, $rs);
2219
2220        $this->triggerComment($id);
2221        $this->triggerBlog();
2222    }
2223
2224    /**
2225    Updates comment status.
2226
2227    @param    id        <b>integer</b>        Comment ID
2228    @param    status    <b>integer</b>        Comment status
2229     */
2230    public function updCommentStatus($id, $status)
2231    {
2232        $this->updCommentsStatus($id, $status);
2233    }
2234
2235    /**
2236    Updates comments status.
2237
2238    @param    ids        <b>mixed</b>        Comment(s) ID(s)
2239    @param    status    <b>integer</b>        Comment status
2240     */
2241    public function updCommentsStatus($ids, $status)
2242    {
2243        if (!$this->core->auth->check('publish,contentadmin', $this->id)) {
2244            throw new Exception(__("You are not allowed to change this comment's status"));
2245        }
2246
2247        $co_ids = dcUtils::cleanIds($ids);
2248        $status = (integer) $status;
2249
2250        $strReq =
2251        'UPDATE ' . $this->prefix . 'comment ' .
2252            'SET comment_status = ' . $status . ' ';
2253        $strReq .=
2254        'WHERE comment_id' . $this->con->in($co_ids) .
2255        'AND post_id in (SELECT tp.post_id ' .
2256        'FROM ' . $this->prefix . 'post tp ' .
2257        "WHERE tp.blog_id = '" . $this->con->escape($this->id) . "' ";
2258        if (!$this->core->auth->check('contentadmin', $this->id)) {
2259            $strReq .=
2260            "AND user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
2261        }
2262        $strReq .= ')';
2263        $this->con->execute($strReq);
2264        $this->triggerComments($co_ids);
2265        $this->triggerBlog();
2266    }
2267
2268    /**
2269    Delete a comment
2270
2271    @param    id        <b>integer</b>        Comment ID
2272     */
2273    public function delComment($id)
2274    {
2275        $this->delComments($id);
2276    }
2277
2278    /**
2279    Delete comments
2280
2281    @param    ids        <b>mixed</b>        Comment(s) ID(s)
2282     */
2283    public function delComments($ids)
2284    {
2285        if (!$this->core->auth->check('delete,contentadmin', $this->id)) {
2286            throw new Exception(__('You are not allowed to delete comments'));
2287        }
2288
2289        $co_ids = dcUtils::cleanIds($ids);
2290
2291        if (empty($co_ids)) {
2292            throw new Exception(__('No such comment ID'));
2293        }
2294
2295        # Retrieve posts affected by comments edition
2296        $affected_posts = array();
2297        $strReq         =
2298        'SELECT post_id ' .
2299        'FROM ' . $this->prefix . 'comment ' .
2300        'WHERE comment_id' . $this->con->in($co_ids) .
2301            'GROUP BY post_id';
2302
2303        $rs = $this->con->select($strReq);
2304
2305        while ($rs->fetch()) {
2306            $affected_posts[] = (integer) $rs->post_id;
2307        }
2308
2309        $strReq =
2310        'DELETE FROM ' . $this->prefix . 'comment ' .
2311        'WHERE comment_id' . $this->con->in($co_ids) . ' ' .
2312        'AND post_id in (SELECT tp.post_id ' .
2313        'FROM ' . $this->prefix . 'post tp ' .
2314        "WHERE tp.blog_id = '" . $this->con->escape($this->id) . "' ";
2315        #If user can only delete, we need to check the post's owner
2316        if (!$this->core->auth->check('contentadmin', $this->id)) {
2317            $strReq .=
2318            "AND tp.user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
2319        }
2320        $strReq .= ")";
2321        $this->con->execute($strReq);
2322        $this->triggerComments($co_ids, true, $affected_posts);
2323        $this->triggerBlog();
2324    }
2325
2326    public function delJunkComments()
2327    {
2328        if (!$this->core->auth->check('delete,contentadmin', $this->id)) {
2329            throw new Exception(__('You are not allowed to delete comments'));
2330        }
2331
2332        $strReq =
2333        'DELETE FROM ' . $this->prefix . 'comment ' .
2334        'WHERE comment_status = -2 ' .
2335        'AND post_id in (SELECT tp.post_id ' .
2336        'FROM ' . $this->prefix . 'post tp ' .
2337        "WHERE tp.blog_id = '" . $this->con->escape($this->id) . "' ";
2338        #If user can only delete, we need to check the post's owner
2339        if (!$this->core->auth->check('contentadmin', $this->id)) {
2340            $strReq .=
2341            "AND tp.user_id = '" . $this->con->escape($this->core->auth->userID()) . "' ";
2342        }
2343        $strReq .= ")";
2344        $this->con->execute($strReq);
2345        $this->triggerBlog();
2346    }
2347
2348    private function getCommentCursor($cur)
2349    {
2350        if ($cur->comment_content !== null && $cur->comment_content == '') {
2351            throw new Exception(__('You must provide a comment'));
2352        }
2353
2354        if ($cur->comment_author !== null && $cur->comment_author == '') {
2355            throw new Exception(__('You must provide an author name'));
2356        }
2357
2358        if ($cur->comment_email != '' && !text::isEmail($cur->comment_email)) {
2359            throw new Exception(__('Email address is not valid.'));
2360        }
2361
2362        if ($cur->comment_site !== null && $cur->comment_site != '') {
2363            if (!preg_match('|^http(s?)://|i', $cur->comment_site, $matches)) {
2364                $cur->comment_site = 'http://' . $cur->comment_site;
2365            } else {
2366                $cur->comment_site = strtolower($matches[0]) . substr($cur->comment_site, strlen($matches[0]));
2367            }
2368        }
2369
2370        if ($cur->comment_status === null) {
2371            $cur->comment_status = (integer) $this->settings->system->comments_pub;
2372        }
2373
2374        # Words list
2375        if ($cur->comment_content !== null) {
2376            $cur->comment_words = implode(' ', text::splitWords($cur->comment_content));
2377        }
2378    }
2379    //@}
2380}
Note: See TracBrowser for help on using the repository browser.

Sites map