Dotclear

source: inc/core/class.dc.core.php @ 2641:1ec355180bff

Revision 2641:1ec355180bff, 36.3 KB checked in by franck <carnet.franck.paul@…>, 12 years ago (diff)

New default theme is now berlin (for new installations).
Minor ARIA complement

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) { return; }
13
14/**
15@defgroup DC_CORE Dotclear Core Classes
16*/
17
18/**
19@ingroup DC_CORE
20@nosubgrouping
21@brief Dotclear core class
22
23True to its name dcCore is the core of Dotclear. It handles everything related
24to blogs, database connection, plugins...
25*/
26class dcCore
27{
28     public $con;        ///< <b>connection</b>        Database connection object
29     public $prefix;          ///< <b>string</b>            Database tables prefix
30     public $blog;       ///< <b>dcBlog</b>            dcBlog object
31     public $error;      ///< <b>dcError</b>           dcError object
32     public $auth;       ///< <b>dcAuth</b>            dcAuth object
33     public $session;    ///< <b>sessionDB</b>         sessionDB object
34     public $url;        ///< <b>urlHandler</b>        urlHandler object
35     public $wiki2xhtml; ///< <b>wiki2xhtml</b>        wiki2xhtml object
36     public $plugins;    ///< <b>dcModules</b>         dcModules object
37     public $media;      ///< <b>dcMedia</b>           dcMedia object
38     public $postmedia;  ///< <b>dcPostMedia</b>       dcPostMedia object
39     public $rest;       ///< <b>dcRestServer</b> dcRestServer object
40     public $log;        ///< <b>dcLog</b>             dcLog object
41     public $stime;      ///< <b>float</b>             starting time
42
43     private $versions = null;
44     private $formaters = array();
45     private $behaviors = array();
46     private $post_types = array();
47
48     /**
49     dcCore constructor inits everything related to Dotclear. It takes arguments
50     to init database connection.
51
52     @param    driver    <b>string</b>  Database driver name
53     @param    host      <b>string</b>  Database hostname
54     @param    db        <b>string</b>  Database name
55     @param    user      <b>string</b>  Database username
56     @param    password  <b>string</b>  Database password
57     @param    prefix    <b>string</b>  DotClear tables prefix
58     @param    persist   <b>boolean</b> Persistent database connection
59     */
60     public function __construct($driver, $host, $db, $user, $password, $prefix, $persist)
61     {
62          if (defined('DC_START_TIME')) {
63               $this->stime=DC_START_TIME;
64          } else {
65               $this->stime = microtime(true);
66          }
67
68          $this->con = dbLayer::init($driver,$host,$db,$user,$password,$persist);
69
70          # define weak_locks for mysql
71          if ($this->con instanceof mysqlConnection) {
72               mysqlConnection::$weak_locks = true;
73          } elseif ($this->con instanceof mysqliConnection) {
74               mysqliConnection::$weak_locks = true;
75          }
76
77          # define searchpath for postgresql
78          if ($this->con instanceof pgsqlConnection)
79          {
80               $searchpath = explode ('.',$prefix,2);
81               if (count($searchpath) > 1)
82               {
83                    $prefix = $searchpath[1];
84                    $sql = 'SET search_path TO '.$searchpath[0].',public;';
85                    $this->con->execute($sql);
86               }
87          }
88
89          $this->prefix = $prefix;
90
91          $this->error = new dcError();
92          $this->auth = $this->authInstance();
93          $this->session = new sessionDB($this->con,$this->prefix.'session',DC_SESSION_NAME,'',null,DC_ADMIN_SSL);
94          $this->url = new dcUrlHandlers();
95
96          $this->plugins = new dcPlugins($this);
97
98          $this->rest = new dcRestServer($this);
99
100          $this->meta = new dcMeta($this);
101
102          $this->log = new dcLog($this);
103
104          $this->addFormater('xhtml', create_function('$s','return $s;'));
105          $this->addFormater('wiki', array($this,'wikiTransform'));
106     }
107
108     private function authInstance()
109     {
110          # You can set DC_AUTH_CLASS to whatever you want.
111          # Your new class *should* inherits dcAuth.
112          if (!defined('DC_AUTH_CLASS')) {
113               $c = 'dcAuth';
114          } else {
115               $c = DC_AUTH_CLASS;
116          }
117
118          if (!class_exists($c)) {
119               throw new Exception('Authentication class '.$c.' does not exist.');
120          }
121
122          if ($c != 'dcAuth' && !is_subclass_of($c,'dcAuth')) {
123               throw new Exception('Authentication class '.$c.' does not inherit dcAuth.');
124          }
125
126          return new $c($this);
127     }
128
129
130     /// @name Blog init methods
131     //@{
132     /**
133     Sets a blog to use in <var>blog</var> property.
134
135     @param    id        <b>string</b>       Blog ID
136     */
137     public function setBlog($id)
138     {
139          $this->blog = new dcBlog($this, $id);
140     }
141
142     /**
143     Unsets <var>blog</var> property.
144     */
145     public function unsetBlog()
146     {
147          $this->blog = null;
148     }
149     //@}
150
151
152     /// @name Blog status methods
153     //@{
154     /**
155     Returns an array of available blog status codes and names.
156
157     @return   <b>array</b> Simple array with codes in keys and names in value
158     */
159     public function getAllBlogStatus()
160     {
161          return array(
162               1 => __('online'),
163               0 => __('offline'),
164               -1 => __('removed')
165          );
166     }
167
168     /**
169     Returns a blog status name given to a code. This is intended to be
170     human-readable and will be translated, so never use it for tests.
171     If status code does not exist, returns <i>offline</i>.
172
173     @param    s    <b>integer</b> Status code
174     @return   <b>string</b> Blog status name
175     */
176     public function getBlogStatus($s)
177     {
178          $r = $this->getAllBlogStatus();
179          if (isset($r[$s])) {
180               return $r[$s];
181          }
182          return $r[0];
183     }
184     //@}
185
186     /// @name Admin nonce secret methods
187     //@{
188
189     public function getNonce()
190     {
191          return crypt::hmac(DC_MASTER_KEY,session_id());
192     }
193
194     public function checkNonce($secret)
195     {
196          if (!preg_match('/^([0-9a-f]{40,})$/i',$secret)) {
197               return false;
198          }
199
200          return $secret == crypt::hmac(DC_MASTER_KEY,session_id());
201     }
202
203     public function formNonce()
204     {
205          if (!session_id()) {
206               return;
207          }
208
209          return form::hidden(array('xd_check'),$this->getNonce());
210     }
211     //@}
212
213
214     /// @name Text Formatters methods
215     //@{
216     /**
217     Adds a new text formater which will call the function <var>$func</var> to
218     transform text. The function must be a valid callback and takes one
219     argument: the string to transform. It returns the transformed string.
220
221     @param    name      <b>string</b>       Formater name
222     @param    func      <b>callback</b>     Function to use, must be a valid and callable callback
223     */
224     public function addFormater($name,$func)
225     {
226          if (is_callable($func)) {
227               $this->formaters[$name] = $func;
228          }
229     }
230
231     /**
232     Returns formaters list.
233
234     @return   <b>array</b> An array of formaters names in values.
235     */
236     public function getFormaters()
237     {
238          return array_keys($this->formaters);
239     }
240
241     /**
242     If <var>$name</var> is a valid formater, it returns <var>$str</var>
243     transformed using that formater.
244
245     @param    name      <b>string</b>       Formater name
246     @param    str       <b>string</b>       String to transform
247     @return   <b>string</b>  String transformed
248     */
249     public function callFormater($name,$str)
250     {
251          if (isset($this->formaters[$name])) {
252               return call_user_func($this->formaters[$name],$str);
253          }
254
255          return $str;
256     }
257     //@}
258
259
260     /// @name Behaviors methods
261     //@{
262     /**
263     Adds a new behavior to behaviors stack. <var>$func</var> must be a valid
264     and callable callback.
265
266     @param    behavior  <b>string</b>       Behavior name
267     @param    func      <b>callback</b>     Function to call
268     */
269     public function addBehavior($behavior,$func)
270     {
271          if (is_callable($func)) {
272               $this->behaviors[$behavior][] = $func;
273          }
274     }
275
276     /**
277     Tests if a particular behavior exists in behaviors stack.
278
279     @param    behavior  <b>string</b>  Behavior name
280     @return   <b>boolean</b>
281     */
282     public function hasBehavior($behavior)
283     {
284          return isset($this->behaviors[$behavior]);
285     }
286
287     /**
288     Get behaviors stack (or part of).
289
290     @param    behavior  <b>string</b>       Behavior name
291     @return   <b>array</b>
292     */
293     public function getBehaviors($behavior='')
294     {
295          if (empty($this->behaviors)) return null;
296
297          if ($behavior == '') {
298               return $this->behaviors;
299          } elseif (isset($this->behaviors[$behavior])) {
300               return $this->behaviors[$behavior];
301          }
302
303          return array();
304     }
305
306     /**
307     Calls every function in behaviors stack for a given behavior and returns
308     concatened result of each function.
309
310     Every parameters added after <var>$behavior</var> will be pass to
311     behavior calls.
312
313     @param    behavior  <b>string</b>  Behavior name
314     @return   <b>string</b> Behavior concatened result
315     */
316     public function callBehavior($behavior)
317     {
318          if (isset($this->behaviors[$behavior]))
319          {
320               $args = func_get_args();
321               array_shift($args);
322
323               $res = '';
324
325               foreach ($this->behaviors[$behavior] as $f) {
326                    $res .= call_user_func_array($f,$args);
327               }
328
329               return $res;
330          }
331     }
332     //@}
333
334     /// @name Post types URLs management
335     //@{
336     public function getPostAdminURL($type,$post_id,$escaped=true)
337     {
338          if (!isset($this->post_types[$type])) {
339               $type = 'post';
340          }
341
342          $url = sprintf($this->post_types[$type]['admin_url'],$post_id);
343          return $escaped ? html::escapeURL($url) : $url;
344     }
345
346     public function getPostPublicURL($type,$post_url,$escaped=true)
347     {
348          if (!isset($this->post_types[$type])) {
349               $type = 'post';
350          }
351
352          $url = sprintf($this->post_types[$type]['public_url'],$post_url);
353          return $escaped ? html::escapeURL($url) : $url;
354     }
355
356     public function setPostType($type,$admin_url,$public_url,$label='')
357     {
358          $this->post_types[$type] = array(
359               'admin_url' => $admin_url,
360               'public_url' => $public_url,
361               'label' => ($label != '' ? $label : $type)
362          );
363     }
364
365     public function getPostTypes()
366     {
367          return $this->post_types;
368     }
369     //@}
370
371     /// @name Versions management methods
372     //@{
373     /**
374     Returns a given $module version.
375
376     @param    module    <b>string</b>  Module name
377     @return   <b>string</b>  Module version
378     */
379     public function getVersion($module='core')
380     {
381          # Fetch versions if needed
382          if (!is_array($this->versions))
383          {
384               $strReq = 'SELECT module, version FROM '.$this->prefix.'version';
385               $rs = $this->con->select($strReq);
386
387               while ($rs->fetch()) {
388                    $this->versions[$rs->module] = $rs->version;
389               }
390          }
391
392          if (isset($this->versions[$module])) {
393               return $this->versions[$module];
394          } else {
395               return null;
396          }
397     }
398
399     /**
400     Sets $version to given $module.
401
402     @param    module    <b>string</b>  Module name
403     @param    version   <b>string</b>  Module version
404     */
405     public function setVersion($module,$version)
406     {
407          $cur_version = $this->getVersion($module);
408
409          $cur = $this->con->openCursor($this->prefix.'version');
410          $cur->module = (string) $module;
411          $cur->version = (string) $version;
412
413          if ($cur_version === null) {
414               $cur->insert();
415          } else {
416               $cur->update("WHERE module='".$this->con->escape($module)."'");
417          }
418
419          $this->versions[$module] = $version;
420     }
421
422     /**
423     Removes given $module version entry.
424
425     @param    module    <b>string</b>  Module name
426     */
427     public function delVersion($module)
428     {
429          $strReq =
430          'DELETE FROM '.$this->prefix.'version '.
431          "WHERE module = '".$this->con->escape($module)."' ";
432
433          $this->con->execute($strReq);
434
435          if (is_array($this->versions)) {
436               unset($this->versions[$module]);
437          }
438     }
439
440     //@}
441
442     /// @name Users management methods
443     //@{
444     /**
445     Returns a user by its ID.
446
447     @param    id        <b>string</b>       User ID
448     @return   <b>record</b>
449     */
450     public function getUser($id)
451     {
452          $params['user_id'] = $id;
453
454          return $this->getUsers($params);
455     }
456
457     /**
458     Returns a users list. <b>$params</b> is an array with the following
459     optionnal parameters:
460
461      - <var>q</var>: search string (on user_id, user_name, user_firstname)
462      - <var>user_id</var>: user ID
463      - <var>order</var>: ORDER BY clause (default: user_id ASC)
464      - <var>limit</var>: LIMIT clause (should be an array ![limit,offset])
465
466     @param    params         <b>array</b>        Parameters
467     @param    count_only     <b>boolean</b>      Only counts results
468     @return   <b>record</b>
469     */
470     public function getUsers($params=array(),$count_only=false)
471     {
472          if ($count_only)
473          {
474               $strReq =
475               'SELECT count(U.user_id) '.
476               'FROM '.$this->prefix.'user U '.
477               'WHERE NULL IS NULL ';
478          }
479          else
480          {
481               $strReq =
482               'SELECT U.user_id,user_super,user_status,user_pwd,user_change_pwd,'.
483               'user_name,user_firstname,user_displayname,user_email,user_url,'.
484               'user_desc, user_lang,user_tz, user_post_status,user_options, '.
485               'count(P.post_id) AS nb_post '.
486               'FROM '.$this->prefix.'user U '.
487                    'LEFT JOIN '.$this->prefix.'post P ON U.user_id = P.user_id '.
488               'WHERE NULL IS NULL ';
489          }
490
491          if (!empty($params['q'])) {
492               $q = $this->con->escape(str_replace('*','%',strtolower($params['q'])));
493               $strReq .= 'AND ('.
494                    "LOWER(U.user_id) LIKE '".$q."' ".
495                    "OR LOWER(user_name) LIKE '".$q."' ".
496                    "OR LOWER(user_firstname) LIKE '".$q."' ".
497                    ') ';
498          }
499
500          if (!empty($params['user_id'])) {
501               $strReq .= "AND U.user_id = '".$this->con->escape($params['user_id'])."' ";
502          }
503
504          if (!$count_only) {
505               $strReq .= 'GROUP BY U.user_id,user_super,user_status,user_pwd,user_change_pwd,'.
506               'user_name,user_firstname,user_displayname,user_email,user_url,'.
507               'user_desc, user_lang,user_tz,user_post_status,user_options ';
508
509               if (!empty($params['order']) && !$count_only) {
510                    $strReq .= 'ORDER BY '.$this->con->escape($params['order']).' ';
511               } else {
512                    $strReq .= 'ORDER BY U.user_id ASC ';
513               }
514          }
515
516          if (!$count_only && !empty($params['limit'])) {
517               $strReq .= $this->con->limit($params['limit']);
518          }
519
520          $rs = $this->con->select($strReq);
521          $rs->extend('rsExtUser');
522          return $rs;
523     }
524
525     /**
526     Create a new user. Takes a cursor as input and returns the new user ID.
527
528     @param    cur       <b>cursor</b>       User cursor
529     @return   <b>string</b>
530     */
531     public function addUser($cur)
532     {
533          if (!$this->auth->isSuperAdmin()) {
534               throw new Exception(__('You are not an administrator'));
535          }
536
537          if ($cur->user_id == '') {
538               throw new Exception(__('No user ID given'));
539          }
540
541          if ($cur->user_pwd == '') {
542               throw new Exception(__('No password given'));
543          }
544
545          $this->getUserCursor($cur);
546
547          if ($cur->user_creadt === null) {
548               $cur->user_creadt = date('Y-m-d H:i:s');
549          }
550
551          $cur->insert();
552
553          $this->auth->afterAddUser($cur);
554
555          return $cur->user_id;
556     }
557
558     /**
559     Updates an existing user. Returns the user ID.
560
561     @param    id        <b>string</b>       User ID
562     @param    cur       <b>cursor</b>       User cursor
563     @return   <b>string</b>
564     */
565     public function updUser($id,$cur)
566     {
567          $this->getUserCursor($cur);
568
569          if (($cur->user_id !== null || $id != $this->auth->userID()) &&
570          !$this->auth->isSuperAdmin()) {
571               throw new Exception(__('You are not an administrator'));
572          }
573
574          $cur->update("WHERE user_id = '".$this->con->escape($id)."' ");
575
576          $this->auth->afterUpdUser($id,$cur);
577
578          if ($cur->user_id !== null) {
579               $id = $cur->user_id;
580          }
581
582          # Updating all user's blogs
583          $rs = $this->con->select(
584               'SELECT DISTINCT(blog_id) FROM '.$this->prefix.'post '.
585               "WHERE user_id = '".$this->con->escape($id)."' "
586               );
587
588          while ($rs->fetch()) {
589               $b = new dcBlog($this,$rs->blog_id);
590               $b->triggerBlog();
591               unset($b);
592          }
593
594          return $id;
595     }
596
597     /**
598     Deletes a user.
599
600     @param    id        <b>string</b>       User ID
601     */
602     public function delUser($id)
603     {
604          if (!$this->auth->isSuperAdmin()) {
605               throw new Exception(__('You are not an administrator'));
606          }
607
608          if ($id == $this->auth->userID()) {
609               return;
610          }
611
612          $rs = $this->getUser($id);
613
614          if ($rs->nb_post > 0) {
615               return;
616          }
617
618          $strReq = 'DELETE FROM '.$this->prefix.'user '.
619                    "WHERE user_id = '".$this->con->escape($id)."' ";
620
621          $this->con->execute($strReq);
622
623          $this->auth->afterDelUser($id);
624     }
625
626     /**
627     Checks whether a user exists.
628
629     @param    id        <b>string</b>       User ID
630     @return   <b>boolean</b>
631     */
632     public function userExists($id)
633     {
634          $strReq = 'SELECT user_id '.
635                    'FROM '.$this->prefix.'user '.
636                    "WHERE user_id = '".$this->con->escape($id)."' ";
637
638          $rs = $this->con->select($strReq);
639
640          return !$rs->isEmpty();
641     }
642
643     /**
644     Returns all user permissions as an array which looks like:
645
646      - [blog_id]
647        - [name] => Blog name
648        - [url] => Blog URL
649        - [p]
650          - [permission] => true
651          - ...
652
653     @param    id        <b>string</b>       User ID
654     @return   <b>array</b>
655     */
656     public function getUserPermissions($id)
657     {
658          $strReq = 'SELECT B.blog_id, blog_name, blog_url, permissions '.
659                    'FROM '.$this->prefix.'permissions P '.
660                    'INNER JOIN '.$this->prefix.'blog B ON P.blog_id = B.blog_id '.
661                    "WHERE user_id = '".$this->con->escape($id)."' ";
662
663          $rs = $this->con->select($strReq);
664
665          $res = array();
666
667          while ($rs->fetch())
668          {
669               $res[$rs->blog_id] = array(
670                    'name' => $rs->blog_name,
671                    'url' => $rs->blog_url,
672                    'p' => $this->auth->parsePermissions($rs->permissions)
673               );
674          }
675
676          return $res;
677     }
678
679     /**
680     Sets user permissions. The <var>$perms</var> array looks like:
681
682      - [blog_id] => '|perm1|perm2|'
683      - ...
684
685     @param    id        <b>string</b>       User ID
686     @param    perms     <b>array</b>        Permissions array
687     */
688     public function setUserPermissions($id,$perms)
689     {
690          if (!$this->auth->isSuperAdmin()) {
691               throw new Exception(__('You are not an administrator'));
692          }
693
694          $strReq = 'DELETE FROM '.$this->prefix.'permissions '.
695                    "WHERE user_id = '".$this->con->escape($id)."' ";
696
697          $this->con->execute($strReq);
698
699          foreach ($perms as $blog_id => $p) {
700               $this->setUserBlogPermissions($id, $blog_id, $p, false);
701          }
702     }
703
704     /**
705     Sets user permissions for a given blog. <var>$perms</var> is an array with
706     permissions in values
707
708     @param    id             <b>string</b>       User ID
709     @param    blog_id        <b>string</b>       Blog ID
710     @param    perms          <b>array</b>        Permissions
711     @param    delete_first   <b>boolean</b>      Delete permissions before
712     */
713     public function setUserBlogPermissions($id, $blog_id, $perms, $delete_first=true)
714     {
715          if (!$this->auth->isSuperAdmin()) {
716               throw new Exception(__('You are not an administrator'));
717          }
718
719          $no_perm = empty($perms);
720
721          $perms = '|'.implode('|',array_keys($perms)).'|';
722
723          $cur = $this->con->openCursor($this->prefix.'permissions');
724
725          $cur->user_id = (string) $id;
726          $cur->blog_id = (string) $blog_id;
727          $cur->permissions = $perms;
728
729          if ($delete_first || $no_perm)
730          {
731               $strReq = 'DELETE FROM '.$this->prefix.'permissions '.
732                         "WHERE blog_id = '".$this->con->escape($blog_id)."' ".
733                         "AND user_id = '".$this->con->escape($id)."' ";
734
735               $this->con->execute($strReq);
736          }
737
738          if (!$no_perm) {
739               $cur->insert();
740          }
741     }
742
743     /**
744     Sets a user default blog. This blog will be selected when user log in.
745
746     @param    id             <b>string</b>       User ID
747     @param    blog_id        <b>string</b>       Blog ID
748     */
749     public function setUserDefaultBlog($id, $blog_id)
750     {
751          $cur = $this->con->openCursor($this->prefix.'user');
752
753          $cur->user_default_blog = (string) $blog_id;
754
755          $cur->update("WHERE user_id = '".$this->con->escape($id)."'");
756     }
757
758     private function getUserCursor($cur)
759     {
760          if ($cur->isField('user_id')
761          && !preg_match('/^[A-Za-z0-9@._-]{2,}$/',$cur->user_id)) {
762               throw new Exception(__('User ID must contain at least 2 characters using letters, numbers or symbols.'));
763          }
764
765          if ($cur->user_url !== null && $cur->user_url != '') {
766               if (!preg_match('|^http(s?)://|',$cur->user_url)) {
767                    $cur->user_url = 'http://'.$cur->user_url;
768               }
769          }
770
771          if ($cur->isField('user_pwd')) {
772               if (strlen($cur->user_pwd) < 6) {
773                    throw new Exception(__('Password must contain at least 6 characters.'));
774               }
775               $cur->user_pwd = crypt::hmac(DC_MASTER_KEY,$cur->user_pwd);
776          }
777
778          if ($cur->user_lang !== null && !preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$cur->user_lang)) {
779               throw new Exception(__('Invalid user language code'));
780          }
781
782          if ($cur->user_upddt === null) {
783               $cur->user_upddt = date('Y-m-d H:i:s');
784          }
785
786          if ($cur->user_options !== null) {
787               $cur->user_options = serialize((array) $cur->user_options);
788          }
789     }
790
791     /**
792     Returns user default settings in an associative array with setting names in
793     keys.
794
795     @return   <b>array</b>
796     */
797     public function userDefaults()
798     {
799          return array(
800               'edit_size' => 24,
801               'enable_wysiwyg' => true,
802               'post_format' => 'wiki'
803          );
804     }
805     //@}
806
807     /// @name Blog management methods
808     //@{
809     /**
810     Returns all blog permissions (users) as an array which looks like:
811
812      - [user_id]
813        - [name] => User name
814        - [firstname] => User firstname
815        - [displayname] => User displayname
816        - [super] => (true|false) super admin
817        - [p]
818          - [permission] => true
819          - ...
820
821     @param    id             <b>string</b>       Blog ID
822     @param    with_super     <b>boolean</b>      Includes super admins in result
823     @return   <b>array</b>
824     */
825     public function getBlogPermissions($id,$with_super=true)
826     {
827          $strReq =
828          'SELECT U.user_id AS user_id, user_super, user_name, user_firstname, '.
829          'user_displayname, user_email, permissions '.
830          'FROM '.$this->prefix.'user U '.
831          'JOIN '.$this->prefix.'permissions P ON U.user_id = P.user_id '.
832          "WHERE blog_id = '".$this->con->escape($id)."' ";
833
834          if ($with_super) {
835               $strReq .=
836               'UNION '.
837               'SELECT U.user_id AS user_id, user_super, user_name, user_firstname, '.
838               "user_displayname, user_email, NULL AS permissions ".
839               'FROM '.$this->prefix.'user U '.
840               'WHERE user_super = 1 ';
841          }
842
843          $rs = $this->con->select($strReq);
844
845          $res = array();
846
847          while ($rs->fetch())
848          {
849               $res[$rs->user_id] = array(
850                    'name' => $rs->user_name,
851                    'firstname' => $rs->user_firstname,
852                    'displayname' => $rs->user_displayname,
853                    'email' => $rs->user_email,
854                    'super' => (boolean) $rs->user_super,
855                    'p' => $this->auth->parsePermissions($rs->permissions)
856               );
857          }
858
859          return $res;
860     }
861
862     /**
863     Returns a blog of given ID.
864
865     @param    id        <b>string</b>       Blog ID
866     @return   <b>record</b>
867     */
868     public function getBlog($id)
869     {
870          $blog = $this->getBlogs(array('blog_id'=>$id));
871
872          if ($blog->isEmpty()) {
873               return false;
874          }
875
876          return $blog;
877     }
878
879     /**
880     Returns a record of blogs. <b>$params</b> is an array with the following
881     optionnal parameters:
882
883      - <var>blog_id</var>: Blog ID
884      - <var>q</var>: Search string on blog_id, blog_name and blog_url
885      - <var>limit</var>: limit results
886
887     @param    params         <b>array</b>        Parameters
888     @param    count_only     <b>boolean</b>      Count only results
889     @return   <b>record</b>
890     */
891     public function getBlogs($params=array(),$count_only=false)
892     {
893          $join = '';    // %1$s
894          $where = '';   // %2$s
895
896          if ($count_only)
897          {
898               $strReq = 'SELECT count(B.blog_id) '.
899                         'FROM '.$this->prefix.'blog B '.
900                         '%1$s '.
901                         'WHERE NULL IS NULL '.
902                         '%2$s ';
903          }
904          else
905          {
906               $strReq =
907               'SELECT B.blog_id, blog_uid, blog_url, blog_name, blog_desc, blog_creadt, '.
908               'blog_upddt, blog_status '.
909               'FROM '.$this->prefix.'blog B '.
910               '%1$s '.
911               'WHERE NULL IS NULL '.
912               '%2$s ';
913
914               if (!empty($params['order'])) {
915                    $strReq .= 'ORDER BY '.$this->con->escape($params['order']).' ';
916               } else {
917                    $strReq .= 'ORDER BY B.blog_id ASC ';
918               }
919
920               if (!empty($params['limit'])) {
921                    $strReq .= $this->con->limit($params['limit']);
922               }
923          }
924
925          if ($this->auth->userID() && !$this->auth->isSuperAdmin())
926          {
927               $join = 'INNER JOIN '.$this->prefix.'permissions PE ON B.blog_id = PE.blog_id ';
928               $where =
929               "AND PE.user_id = '".$this->con->escape($this->auth->userID())."' ".
930               "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') ".
931               "AND blog_status IN (1,0) ";
932          } elseif (!$this->auth->userID()) {
933               $where = 'AND blog_status IN (1,0) ';
934          }
935
936          if (!empty($params['blog_id'])) {
937               $where .= "AND B.blog_id = '".$this->con->escape($params['blog_id'])."' ";
938          }
939
940          if (!empty($params['q'])) {
941               $params['q'] = strtolower(str_replace('*','%',$params['q']));
942               $where .=
943               'AND ('.
944               "LOWER(B.blog_id) LIKE '".$this->con->escape($params['q'])."' ".
945               "OR LOWER(B.blog_name) LIKE '".$this->con->escape($params['q'])."' ".
946               "OR LOWER(B.blog_url) LIKE '".$this->con->escape($params['q'])."' ".
947               ') ';
948          }
949
950          $strReq = sprintf($strReq,$join,$where);
951          return $this->con->select($strReq);
952     }
953
954     /**
955     Creates a new blog.
956
957     @param    cur            <b>cursor</b>       Blog cursor
958     */
959     public function addBlog($cur)
960     {
961          if (!$this->auth->isSuperAdmin()) {
962               throw new Exception(__('You are not an administrator'));
963          }
964
965          $this->getBlogCursor($cur);
966
967          $cur->blog_creadt = date('Y-m-d H:i:s');
968          $cur->blog_upddt = date('Y-m-d H:i:s');
969          $cur->blog_uid = md5(uniqid());
970
971          $cur->insert();
972     }
973
974     /**
975     Updates a given blog.
976
977     @param    id        <b>string</b>       Blog ID
978     @param    cur       <b>cursor</b>       Blog cursor
979     */
980     public function updBlog($id,$cur)
981     {
982          $this->getBlogCursor($cur);
983
984          $cur->blog_upddt = date('Y-m-d H:i:s');
985
986          $cur->update("WHERE blog_id = '".$this->con->escape($id)."'");
987     }
988
989     private function getBlogCursor($cur)
990     {
991          if (($cur->blog_id !== null
992               && !preg_match('/^[A-Za-z0-9._-]{2,}$/',$cur->blog_id)) ||
993               (!$cur->blog_id)) {
994               throw new Exception(__('Blog ID must contain at least 2 characters using letters, numbers or symbols.'));
995          }
996
997          if (($cur->blog_name !== null && $cur->blog_name == '') ||
998               (!$cur->blog_name)) {
999               throw new Exception(__('No blog name'));
1000          }
1001
1002          if (($cur->blog_url !== null && $cur->blog_url == '') ||
1003               (!$cur->blog_url)) {
1004               throw new Exception(__('No blog URL'));
1005          }
1006
1007          if ($cur->blog_desc !== null) {
1008               $cur->blog_desc = html::clean($cur->blog_desc);
1009          }
1010     }
1011
1012     /**
1013     Removes a given blog.
1014     @warning This will remove everything related to the blog (posts,
1015     categories, comments, links...)
1016
1017     @param    id        <b>string</b>       Blog ID
1018     */
1019     public function delBlog($id)
1020     {
1021          if (!$this->auth->isSuperAdmin()) {
1022               throw new Exception(__('You are not an administrator'));
1023          }
1024
1025          $strReq = 'DELETE FROM '.$this->prefix.'blog '.
1026                    "WHERE blog_id = '".$this->con->escape($id)."' ";
1027
1028          $this->con->execute($strReq);
1029     }
1030
1031     /**
1032     Checks if a blog exist.
1033
1034     @param    id        <b>string</b>       Blog ID
1035     @return   <b>boolean</b>
1036     */
1037     public function blogExists($id)
1038     {
1039          $strReq = 'SELECT blog_id '.
1040                    'FROM '.$this->prefix.'blog '.
1041                    "WHERE blog_id = '".$this->con->escape($id)."' ";
1042
1043          $rs = $this->con->select($strReq);
1044
1045          return !$rs->isEmpty();
1046     }
1047
1048     /**
1049     Count posts on a blog
1050
1051     @param    id        <b>string</b>       Blog ID
1052     @param    type      <b>string</b>       Post type
1053     @return   <b>boolean</b>
1054     */
1055     public function countBlogPosts($id,$type=null)
1056     {
1057          $strReq = 'SELECT COUNT(post_id) '.
1058                    'FROM '.$this->prefix.'post '.
1059                    "WHERE blog_id = '".$this->con->escape($id)."' ";
1060
1061          if ($type) {
1062               $strReq .= "AND post_type = '".$this->con->escape($type)."' ";
1063          }
1064
1065          return $this->con->select($strReq)->f(0);
1066     }
1067     //@}
1068
1069     /// @name HTML Filter methods
1070     //@{
1071     /**
1072     Calls HTML filter to drop bad tags and produce valid XHTML output (if
1073     tidy extension is present). If <b>enable_html_filter</b> blog setting is
1074     false, returns not filtered string.
1075
1076     @param    str  <b>string</b>       String to filter
1077     @return   <b>string</b> Filtered string.
1078     */
1079     public function HTMLfilter($str)
1080     {
1081          if ($this->blog instanceof dcBlog && !$this->blog->settings->system->enable_html_filter) {
1082               return $str;
1083          }
1084
1085          $filter = new htmlFilter;
1086          $str = trim($filter->apply($str));
1087          return $str;
1088     }
1089     //@}
1090
1091     /// @name wiki2xhtml methods
1092     //@{
1093     private function initWiki()
1094     {
1095          $this->wiki2xhtml = new wiki2xhtml;
1096     }
1097
1098     /**
1099     Returns a transformed string with wiki2xhtml.
1100
1101     @param    str       <b>string</b>       String to transform
1102     @return   <b>string</b>  Transformed string
1103     */
1104     public function wikiTransform($str)
1105     {
1106          if (!($this->wiki2xhtml instanceof wiki2xhtml)) {
1107               $this->initWiki();
1108          }
1109          return $this->wiki2xhtml->transform($str);
1110     }
1111
1112     /**
1113     Inits <var>wiki2xhtml</var> property for blog post.
1114     */
1115     public function initWikiPost()
1116     {
1117          $this->initWiki();
1118
1119          $this->wiki2xhtml->setOpts(array(
1120               'active_title' => 1,
1121               'active_setext_title' => 0,
1122               'active_hr' => 1,
1123               'active_lists' => 1,
1124               'active_quote' => 1,
1125               'active_pre' => 1,
1126               'active_empty' => 1,
1127               'active_auto_br' => 0,
1128               'active_auto_urls' => 0,
1129               'active_urls' => 1,
1130               'active_auto_img' => 0,
1131               'active_img' => 1,
1132               'active_anchor' => 1,
1133               'active_em' => 1,
1134               'active_strong' => 1,
1135               'active_br' => 1,
1136               'active_q' => 1,
1137               'active_code' => 1,
1138               'active_acronym' => 1,
1139               'active_ins' => 1,
1140               'active_del' => 1,
1141               'active_footnotes' => 1,
1142               'active_wikiwords' => 0,
1143               'active_macros' => 1,
1144               'parse_pre' => 1,
1145               'active_fr_syntax' => 0,
1146               'first_title_level' => 3,
1147               'note_prefix' => 'wiki-footnote',
1148               'note_str' => '<div class="footnotes"><h4>Notes</h4>%s</div>'
1149          ));
1150
1151          $this->wiki2xhtml->registerFunction('url:post',array($this,'wikiPostLink'));
1152
1153          # --BEHAVIOR-- coreWikiPostInit
1154          $this->callBehavior('coreInitWikiPost',$this->wiki2xhtml);
1155     }
1156
1157     /**
1158     Inits <var>wiki2xhtml</var> property for simple blog comment (basic syntax).
1159     */
1160     public function initWikiSimpleComment()
1161     {
1162          $this->initWiki();
1163
1164          $this->wiki2xhtml->setOpts(array(
1165               'active_title' => 0,
1166               'active_setext_title' => 0,
1167               'active_hr' => 0,
1168               'active_lists' => 0,
1169               'active_quote' => 0,
1170               'active_pre' => 0,
1171               'active_empty' => 0,
1172               'active_auto_br' => 1,
1173               'active_auto_urls' => 1,
1174               'active_urls' => 0,
1175               'active_auto_img' => 0,
1176               'active_img' => 0,
1177               'active_anchor' => 0,
1178               'active_em' => 0,
1179               'active_strong' => 0,
1180               'active_br' => 0,
1181               'active_q' => 0,
1182               'active_code' => 0,
1183               'active_acronym' => 0,
1184               'active_ins' => 0,
1185               'active_del' => 0,
1186               'active_footnotes' => 0,
1187               'active_wikiwords' => 0,
1188               'active_macros' => 0,
1189               'parse_pre' => 0,
1190               'active_fr_syntax' => 0
1191          ));
1192
1193          # --BEHAVIOR-- coreInitWikiSimpleComment
1194          $this->callBehavior('coreInitWikiSimpleComment',$this->wiki2xhtml);
1195     }
1196
1197     /**
1198     Inits <var>wiki2xhtml</var> property for blog comment.
1199     */
1200     public function initWikiComment()
1201     {
1202          $this->initWiki();
1203
1204          $this->wiki2xhtml->setOpts(array(
1205               'active_title' => 0,
1206               'active_setext_title' => 0,
1207               'active_hr' => 0,
1208               'active_lists' => 1,
1209               'active_quote' => 0,
1210               'active_pre' => 1,
1211               'active_empty' => 0,
1212               'active_auto_br' => 1,
1213               'active_auto_urls' => 1,
1214               'active_urls' => 1,
1215               'active_auto_img' => 0,
1216               'active_img' => 0,
1217               'active_anchor' => 0,
1218               'active_em' => 1,
1219               'active_strong' => 1,
1220               'active_br' => 1,
1221               'active_q' => 1,
1222               'active_code' => 1,
1223               'active_acronym' => 1,
1224               'active_ins' => 1,
1225               'active_del' => 1,
1226               'active_footnotes' => 0,
1227               'active_wikiwords' => 0,
1228               'active_macros' => 0,
1229               'parse_pre' => 0,
1230               'active_fr_syntax' => 0
1231          ));
1232
1233          # --BEHAVIOR-- coreInitWikiComment
1234          $this->callBehavior('coreInitWikiComment',$this->wiki2xhtml);
1235     }
1236
1237     public function wikiPostLink($url,$content)
1238     {
1239          if (!($this->blog instanceof dcBlog)) {
1240               return array();
1241          }
1242
1243          $post_id = abs((integer) substr($url,5));
1244          if (!$post_id) {
1245               return array();
1246          }
1247
1248          $post = $this->blog->getPosts(array('post_id'=>$post_id));
1249          if ($post->isEmpty()) {
1250               return array();
1251          }
1252
1253          $res = array('url' => $post->getURL());
1254          $post_title = $post->post_title;
1255
1256          if ($content != $url) {
1257               $res['title'] = html::escapeHTML($post->post_title);
1258          }
1259
1260          if ($content == '' || $content == $url) {
1261               $res['content'] = html::escapeHTML($post->post_title);
1262          }
1263
1264          if ($post->post_lang) {
1265               $res['lang'] = $post->post_lang;
1266          }
1267
1268          return $res;
1269     }
1270     //@}
1271
1272     /// @name Maintenance methods
1273     //@{
1274     /**
1275     Creates default settings for active blog. Optionnal parameter
1276     <var>defaults</var> replaces default params while needed.
1277
1278     @param    defaults       <b>array</b>   Default parameters
1279     */
1280     public function blogDefaults($defaults=null)
1281     {
1282          if (!is_array($defaults))
1283          {
1284               $defaults = array(
1285                    array('allow_comments','boolean',true,
1286                    'Allow comments on blog'),
1287                    array('allow_trackbacks','boolean',true,
1288                    'Allow trackbacks on blog'),
1289                    array('blog_timezone','string','Europe/London',
1290                    'Blog timezone'),
1291                    array('comments_nofollow','boolean',true,
1292                    'Add rel="nofollow" to comments URLs'),
1293                    array('comments_pub','boolean',true,
1294                    'Publish comments immediately'),
1295                    array('comments_ttl','integer',0,
1296                    'Number of days to keep comments open (0 means no ttl)'),
1297                    array('copyright_notice','string','','Copyright notice (simple text)'),
1298                    array('date_format','string','%A, %B %e %Y',
1299                    'Date format. See PHP strftime function for patterns'),
1300                    array('editor','string','',
1301                    'Person responsible of the content'),
1302                    array('enable_html_filter','boolean',0,
1303                    'Enable HTML filter'),
1304                    array('enable_xmlrpc','boolean',0,
1305                    'Enable XML/RPC interface'),
1306                    array('lang','string','en',
1307                    'Default blog language'),
1308                    array('media_exclusion','string','/\.php$/i',
1309                    'File name exclusion pattern in media manager. (PCRE value)'),
1310                    array('media_img_m_size','integer',448,
1311                    'Image medium size in media manager'),
1312                    array('media_img_s_size','integer',240,
1313                    'Image small size in media manager'),
1314                    array('media_img_t_size','integer',100,
1315                    'Image thumbnail size in media manager'),
1316                    array('media_img_title_pattern','string','Title ;; Date(%b %Y) ;; separator(, )',
1317                    'Pattern to set image title when you insert it in a post'),
1318                    array('nb_post_for_home','integer',20,
1319                    'Number of entries on first home page'),
1320                    array('nb_post_per_page','integer',20,
1321                    'Number of entries on home pages and category pages'),
1322                    array('nb_post_per_feed','integer',20,
1323                    'Number of entries on feeds'),
1324                    array('nb_comment_per_feed','integer',20,
1325                    'Number of comments on feeds'),
1326                    array('post_url_format','string','{y}/{m}/{d}/{t}',
1327                    'Post URL format. {y}: year, {m}: month, {d}: day, {id}: post id, {t}: entry title'),
1328                    array('public_path','string','public',
1329                    'Path to public directory, begins with a / for a full system path'),
1330                    array('public_url','string','/public',
1331                    'URL to public directory'),
1332                    array('robots_policy','string','INDEX,FOLLOW',
1333                    'Search engines robots policy'),
1334                    array('short_feed_items','boolean',false,
1335                    'Display short feed items'),
1336                    array('theme','string','berlin',
1337                    'Blog theme'),
1338                    array('themes_path','string','themes',
1339                    'Themes root path'),
1340                    array('themes_url','string','/themes',
1341                    'Themes root URL'),
1342                    array('time_format','string','%H:%M',
1343                    'Time format. See PHP strftime function for patterns'),
1344                    array('tpl_allow_php','boolean',false,
1345                    'Allow PHP code in templates'),
1346                    array('tpl_use_cache','boolean',true,
1347                    'Use template caching'),
1348                    array('trackbacks_pub','boolean',true,
1349                    'Publish trackbacks immediately'),
1350                    array('trackbacks_ttl','integer',0,
1351                    'Number of days to keep trackbacks open (0 means no ttl)'),
1352                    array('url_scan','string','query_string',
1353                    'URL handle mode (path_info or query_string)'),
1354                    array('use_smilies','boolean',false,
1355                    'Show smilies on entries and comments'),
1356                    array('inc_subcats','boolean',false,
1357                    'Include sub-categories in category page and category posts feed'),
1358                    array('wiki_comments','boolean',false,
1359                    'Allow commenters to use a subset of wiki syntax')
1360               );
1361          }
1362
1363          $settings = new dcSettings($this,null);
1364          $settings->addNamespace('system');
1365
1366          foreach ($defaults as $v) {
1367               $settings->system->put($v[0],$v[2],$v[1],$v[3],false,true);
1368          }
1369     }
1370
1371     /**
1372     Recreates entries search engine index.
1373
1374     @param    start     <b>integer</b>      Start entry index
1375     @param    limit     <b>integer</b>      Number of entry to index
1376
1377     @return   <b>integer</b>      <var>$start</var> and <var>$limit</var> sum
1378     */
1379     public function indexAllPosts($start=null,$limit=null)
1380     {
1381          $strReq = 'SELECT COUNT(post_id) '.
1382                    'FROM '.$this->prefix.'post';
1383          $rs = $this->con->select($strReq);
1384          $count = $rs->f(0);
1385
1386          $strReq = 'SELECT post_id, post_title, post_excerpt_xhtml, post_content_xhtml '.
1387                    'FROM '.$this->prefix.'post ';
1388
1389          if ($start !== null && $limit !== null) {
1390               $strReq .= $this->con->limit($start,$limit);
1391          }
1392
1393          $rs = $this->con->select($strReq,true);
1394
1395          $cur = $this->con->openCursor($this->prefix.'post');
1396
1397          while ($rs->fetch())
1398          {
1399               $words = $rs->post_title.' '. $rs->post_excerpt_xhtml.' '.
1400               $rs->post_content_xhtml;
1401
1402               $cur->post_words = implode(' ',text::splitWords($words));
1403               $cur->update('WHERE post_id = '.(integer) $rs->post_id);
1404               $cur->clean();
1405          }
1406
1407          if ($start+$limit > $count) {
1408               return null;
1409          }
1410          return $start+$limit;
1411     }
1412
1413     /**
1414     Recreates comments search engine index.
1415
1416     @param    start     <b>integer</b>      Start comment index
1417     @param    limit     <b>integer</b>      Number of comments to index
1418
1419     @return   <b>integer</b>      <var>$start</var> and <var>$limit</var> sum
1420     */
1421     public function indexAllComments($start=null,$limit=null)
1422     {
1423          $strReq = 'SELECT COUNT(comment_id) '.
1424                    'FROM '.$this->prefix.'comment';
1425          $rs = $this->con->select($strReq);
1426          $count = $rs->f(0);
1427
1428          $strReq = 'SELECT comment_id, comment_content '.
1429                    'FROM '.$this->prefix.'comment ';
1430
1431          if ($start !== null && $limit !== null) {
1432               $strReq .= $this->con->limit($start,$limit);
1433          }
1434
1435          $rs = $this->con->select($strReq);
1436
1437          $cur = $this->con->openCursor($this->prefix.'comment');
1438
1439          while ($rs->fetch())
1440          {
1441               $cur->comment_words = implode(' ',text::splitWords($rs->comment_content));
1442               $cur->update('WHERE comment_id = '.(integer) $rs->comment_id);
1443               $cur->clean();
1444          }
1445
1446          if ($start+$limit > $count) {
1447               return null;
1448          }
1449          return $start+$limit;
1450     }
1451
1452     /**
1453     Reinits nb_comment and nb_trackback in post table.
1454     */
1455     public function countAllComments()
1456     {
1457
1458          $updCommentReq = 'UPDATE '.$this->prefix.'post P '.
1459               'SET nb_comment = ('.
1460                    'SELECT COUNT(C.comment_id) from '.$this->prefix.'comment C '.
1461                    'WHERE C.post_id = P.post_id AND C.comment_trackback <> 1 '.
1462                    'AND C.comment_status = 1 '.
1463               ')';
1464          $updTrackbackReq = 'UPDATE '.$this->prefix.'post P '.
1465               'SET nb_trackback = ('.
1466                    'SELECT COUNT(C.comment_id) from '.$this->prefix.'comment C '.
1467                    'WHERE C.post_id = P.post_id AND C.comment_trackback = 1 '.
1468                    'AND C.comment_status = 1 '.
1469               ')';
1470          $this->con->execute($updCommentReq);
1471          $this->con->execute($updTrackbackReq);
1472     }
1473
1474     /**
1475     Empty templates cache directory
1476     */
1477     public function emptyTemplatesCache()
1478     {
1479          if (is_dir(DC_TPL_CACHE.'/cbtpl')) {
1480               files::deltree(DC_TPL_CACHE.'/cbtpl');
1481          }
1482     }
1483
1484     /**
1485      Return elapsed time since script has been started
1486      @param   $mtime <b>float</b> timestamp (microtime format) to evaluate delta from
1487                                     current time is taken if null
1488      @return <b>float</b>        elapsed time
1489      */
1490     public function getElapsedTime ($mtime=null) {
1491          if ($mtime !== null) {
1492               return $mtime-$this->stime;
1493          } else {
1494               return microtime(true)-$this->stime;
1495          }
1496     }
1497     //@}
1498
1499
1500
1501}
Note: See TracBrowser for help on using the repository browser.

Sites map