Dotclear

source: inc/core/class.dc.core.php @ 2679:bae19d3adbc4

Revision 2679:bae19d3adbc4, 37.9 KB checked in by Nicolas <nikrou77@…>, 11 years ago (diff)

Addresses #1896 : add choice of editor in preferences
Keeps old functions in core addFormater(), callFormater() for compatibility.

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

Sites map