Dotclear

source: inc/core/class.dc.core.php @ 2609:c26642f775e2

Revision 2609:c26642f775e2, 37.8 KB checked in by Dsls, 12 years ago (diff)

Merge with default

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

Sites map