Dotclear

source: inc/core/class.dc.blog.php @ 3761:849987324197

Revision 3761:849987324197, 79.6 KB checked in by franck <carnet.franck.paul@…>, 7 years ago (diff)

Apply SQL Statement in DC code, work in progress

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

Sites map