Dotclear

source: inc/core/class.dc.blog.php @ 0:54703be25dd6

Revision 0:54703be25dd6, 54.1 KB checked in by Dsls <dsls@…>, 14 years ago (diff)

2.3 branch (trunk) first checkin

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

Sites map