Dotclear

source: inc/public/lib.urlhandlers.php @ 306:dbcdebc54a75

Revision 306:dbcdebc54a75, 15.7 KB checked in by Dsls <dsls@…>, 14 years ago (diff)

Added pre-contextual behaviors in urlhandlers, enables plugins to add their own context parameters for default urlhandlers.

New behaviors introduced (syntax : "public".<urlhandler>."before".<function_called>)

  • publicLangBeforeGetLangs
  • publicCategoryBeforeGetCategories
  • publicArchiveBeforeGetDates
  • publicPostBeforeGetPosts
  • publicFeedBeforeGetLangs
  • publicFeedBeforeGetCategories
  • publicFeedBeforeGetPosts

Closes #1090.

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2011 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
14class dcUrlHandlers extends urlHandler
15{
16     public $args;
17     
18     public static function p404()
19     {
20          throw new Exception ("Page not found",404);
21     }
22     
23     public static function default404($args,$type,$e)
24     {
25          if ($e->getCode() != 404) {
26               throw $e;
27          }
28          $_ctx =& $GLOBALS['_ctx'];
29          $core = $GLOBALS['core'];
30         
31          header('Content-Type: text/html; charset=UTF-8');
32          http::head(404,'Not Found');
33          $core->url->type = '404';
34          $_ctx->current_tpl = '404.html';
35          $_ctx->content_type = 'text/html';
36         
37          echo $core->tpl->getData($_ctx->current_tpl);
38         
39          # --BEHAVIOR-- publicAfterDocument
40          $core->callBehavior('publicAfterDocument',$core);
41          exit;
42     }
43     
44     protected static function getPageNumber(&$args)
45     {
46          if (preg_match('#(^|/)page/([0-9]+)$#',$args,$m)) {
47               $n = (integer) $m[2];
48               if ($n > 0) {
49                    $args = preg_replace('#(^|/)page/([0-9]+)$#','',$args);
50                    return $n;
51               }
52          }
53         
54          return false;
55     }
56     
57     protected static function serveDocument($tpl,$content_type='text/html',$http_cache=true,$http_etag=true)
58     {
59          $_ctx =& $GLOBALS['_ctx'];
60          $core =& $GLOBALS['core'];
61         
62          if ($_ctx->nb_entry_per_page === null) {
63               $_ctx->nb_entry_per_page = $core->blog->settings->system->nb_post_per_page;
64          }
65         
66          $tpl_file = $core->tpl->getFilePath($tpl);
67         
68          if (!$tpl_file) {
69               throw new Exception('Unable to find template ');
70          }
71         
72          $result = new ArrayObject;
73         
74          $_ctx->current_tpl = $tpl;
75          $_ctx->content_type = $content_type;
76          $_ctx->http_cache = $http_cache;
77          $_ctx->http_etag = $http_etag;
78          $core->callBehavior('urlHandlerBeforeGetData',$_ctx);
79         
80          if ($_ctx->http_cache) {
81               $GLOBALS['mod_files'][] = $tpl_file;
82               http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']);
83          }
84
85          header('Content-Type: '.$_ctx->content_type.'; charset=UTF-8');
86          $result['content'] = $core->tpl->getData($_ctx->current_tpl);
87          $result['content_type'] = $_ctx->content_type;
88          $result['tpl'] = $_ctx->current_tpl;
89          $result['blogupddt'] = $core->blog->upddt;
90         
91          # --BEHAVIOR-- urlHandlerServeDocument
92          $core->callBehavior('urlHandlerServeDocument',$result);
93         
94          if ($_ctx->http_cache && $_ctx->http_etag) {
95               http::etag($result['content'],http::getSelfURI());
96          }
97          echo $result['content'];
98     }
99     
100     public function getDocument()
101     {
102          $core =& $GLOBALS['core'];
103         
104          $type = $args = '';
105         
106          if ($this->mode == 'path_info')
107          {
108               $part = substr($_SERVER['PATH_INFO'],1);
109          }
110          else
111          {
112               $part = '';
113               
114               $qs = $this->parseQueryString();
115               
116               # Recreates some _GET and _REQUEST pairs
117               if (!empty($qs))
118               {
119                    foreach ($_GET as $k => $v) {
120                         if (isset($_REQUEST[$k])) {
121                              unset($_REQUEST[$k]);
122                         }
123                    }
124                    $_GET = $qs;
125                    $_REQUEST = array_merge($qs,$_REQUEST);
126                   
127                    list($k,$v) = each($qs);
128                    if ($v === null) {
129                         $part = $k;
130                         unset($_GET[$k]);
131                         unset($_REQUEST[$k]);
132                    }
133               }
134          }
135         
136          $_SERVER['URL_REQUEST_PART'] = $part;
137         
138          $this->getArgs($part,$type,$this->args);
139         
140          # --BEHAVIOR-- urlHandlerGetArgsDocument
141          $core->callBehavior('urlHandlerGetArgsDocument',$this);
142         
143          if (!$type)
144          {
145               $this->type = 'default';
146               $this->callDefaultHandler($this->args);
147          }
148          else
149          {
150               $this->type = $type;
151               $this->callHandler($type,$this->args);
152          }
153     }
154     
155     public static function home($args)
156     {
157          $n = self::getPageNumber($args);
158         
159          if ($args && !$n)
160          {
161               # "Then specified URL went unrecognized by all URL handlers and
162               # defaults to the home page, but is not a page number.
163               self::p404();
164          }
165          else
166          {
167               $core =& $GLOBALS['core'];
168               
169               if ($n) {
170                    $GLOBALS['_page_number'] = $n;
171                    $core->url->type = $n > 1 ? 'default-page' : 'default';
172               }
173               
174               if (empty($_GET['q'])) {
175                    self::serveDocument('home.html');
176                    $core->blog->publishScheduledEntries();
177               } else {
178                    self::search();
179               }
180          }
181     }
182     
183     public static function search()
184     {
185          $_ctx =& $GLOBALS['_ctx'];
186          $core =& $GLOBALS['core'];
187         
188          $core->url->type='search';
189         
190          $GLOBALS['_search'] = !empty($_GET['q']) ? rawurldecode($_GET['q']) : '';
191          if ($GLOBALS['_search']) {
192               $params = new ArrayObject(array('search' => $GLOBALS['_search']));
193               $core->callBehavior('publicBeforeSearchCount',$params);
194               $GLOBALS['_search_count'] = $core->blog->getPosts($params,true)->f(0);
195          }
196         
197          self::serveDocument('search.html');
198     }
199     
200     public static function lang($args)
201     {
202          $_ctx =& $GLOBALS['_ctx'];
203          $core =& $GLOBALS['core'];
204         
205          $n = self::getPageNumber($args);
206          $params = new ArrayObject(array(
207               'lang' => $args));
208         
209          $core->callBehavior('publicLangBeforeGetLangs',$params,$args);
210         
211          $_ctx->langs = $core->blog->getLangs($params);
212         
213          if ($_ctx->langs->isEmpty()) {
214               # The specified language does not exist.
215               self::p404();
216          }
217          else
218          {
219               if ($n) {
220                    $GLOBALS['_page_number'] = $n;
221               }
222               $_ctx->cur_lang = $args;
223               self::home(null);
224          }
225     }
226     
227     public static function category($args)
228     {
229          $_ctx =& $GLOBALS['_ctx'];
230          $core =& $GLOBALS['core'];
231         
232          $n = self::getPageNumber($args);
233         
234          if ($args == '' && !$n) {
235               # No category was specified.
236               self::p404();
237          }
238          else
239          {
240               $params = new ArrayObject(array(
241                    'cat_url' => $args,
242                    'post_type' => 'post'));
243               
244               $core->callBehavior('publicCategoryBeforeGetCategories',$params,$args);
245               
246               $_ctx->categories = $core->blog->getCategories($params);
247               
248               if ($_ctx->categories->isEmpty()) {
249                    # The specified category does no exist.
250                    self::p404();
251               }
252               else
253               {
254                    if ($n) {
255                         $GLOBALS['_page_number'] = $n;
256                    }
257                    self::serveDocument('category.html');
258               }
259          }
260     }
261     
262     public static function archive($args)
263     {
264          $_ctx =& $GLOBALS['_ctx'];
265          $core =& $GLOBALS['core'];
266         
267          $year = $month = $cat_url = null;
268          # Nothing or year and month
269          if ($args == '')
270          {
271               self::serveDocument('archive.html');
272          }
273          elseif (preg_match('|^/([0-9]{4})/([0-9]{2})$|',$args,$m))
274          {
275               $params = new ArrayObject(array(
276                    'year' => $m[1],
277                    'month' => $m[2],
278                    'type' => 'month'));
279               
280               $core->callBehavior('publicArchiveBeforeGetDates',$params,$args);
281               
282               $_ctx->archives = $core->blog->getDates($params);
283               
284               if ($_ctx->archives->isEmpty()) {
285                    # There is no entries for the specified period.
286                    self::p404();
287               }
288               else
289               {
290                    self::serveDocument('archive_month.html');
291               }
292          }
293          else {
294               # The specified URL is not a date.
295               self::p404();
296          }
297     }
298     
299     public static function post($args)
300     {
301          if ($args == '') {
302               # No entry was specified.
303               self::p404();
304          }
305          else
306          {
307               $_ctx =& $GLOBALS['_ctx'];
308               $core =& $GLOBALS['core'];
309               
310               $core->blog->withoutPassword(false);
311               
312               $params = new ArrayObject(array(
313                    'post_url' => $args));
314               
315               $core->callBehavior('publicPostBeforeGetPosts',$params,$args);
316
317               $_ctx->posts = $core->blog->getPosts($params);
318               
319               $_ctx->comment_preview = new ArrayObject();
320               $_ctx->comment_preview['content'] = '';
321               $_ctx->comment_preview['rawcontent'] = '';
322               $_ctx->comment_preview['name'] = '';
323               $_ctx->comment_preview['mail'] = '';
324               $_ctx->comment_preview['site'] = '';
325               $_ctx->comment_preview['preview'] = false;
326               $_ctx->comment_preview['remember'] = false;
327               
328               $core->blog->withoutPassword(true);
329               
330               if ($_ctx->posts->isEmpty())
331               {
332                    # The specified entry does not exist.
333                    self::p404();
334               }
335               else
336               {
337                    $post_id = $_ctx->posts->post_id;
338                    $post_password = $_ctx->posts->post_password;
339                   
340                    # Password protected entry
341                    if ($post_password != '' && !$_ctx->preview)
342                    {
343                         # Get passwords cookie
344                         if (isset($_COOKIE['dc_passwd'])) {
345                              $pwd_cookie = unserialize($_COOKIE['dc_passwd']);
346                         } else {
347                              $pwd_cookie = array();
348                         }
349                         
350                         # Check for match
351                         if ((!empty($_POST['password']) && $_POST['password'] == $post_password)
352                         || (isset($pwd_cookie[$post_id]) && $pwd_cookie[$post_id] == $post_password))
353                         {
354                              $pwd_cookie[$post_id] = $post_password;
355                              setcookie('dc_passwd',serialize($pwd_cookie),0,'/');
356                         }
357                         else
358                         {
359                              self::serveDocument('password-form.html','text/html',false);
360                              return;
361                         }
362                    }
363                   
364                    $post_comment =
365                         isset($_POST['c_name']) && isset($_POST['c_mail']) &&
366                         isset($_POST['c_site']) && isset($_POST['c_content']) &&
367                         $_ctx->posts->commentsActive();
368                   
369                    # Posting a comment
370                    if ($post_comment)
371                    {
372                         # Spam trap
373                         if (!empty($_POST['f_mail'])) {
374                              http::head(412,'Precondition Failed');
375                              header('Content-Type: text/plain');
376                              echo "So Long, and Thanks For All the Fish";
377                              # Exits immediately the application to preserve the server.
378                              exit;
379                         }
380                         
381                         $name = $_POST['c_name'];
382                         $mail = $_POST['c_mail'];
383                         $site = $_POST['c_site'];
384                         $content = $_POST['c_content'];
385                         $preview = !empty($_POST['preview']);
386                         
387                         if ($content != '')
388                         {
389                              if ($core->blog->settings->system->wiki_comments) {
390                                   $core->initWikiComment();
391                              } else {
392                                   $core->initWikiSimpleComment();
393                              }
394                              $content = $core->wikiTransform($content);
395                              $content = $core->HTMLfilter($content);
396                         }
397                         
398                         $_ctx->comment_preview['content'] = $content;
399                         $_ctx->comment_preview['rawcontent'] = $_POST['c_content'];
400                         $_ctx->comment_preview['name'] = $name;
401                         $_ctx->comment_preview['mail'] = $mail;
402                         $_ctx->comment_preview['site'] = $site;
403                         
404                         if ($preview)
405                         {
406                              # --BEHAVIOR-- publicBeforeCommentPreview
407                              $core->callBehavior('publicBeforeCommentPreview',$_ctx->comment_preview);
408                             
409                              $_ctx->comment_preview['preview'] = true;
410                         }
411                         else
412                         {
413                              # Post the comment
414                              $cur = $core->con->openCursor($core->prefix.'comment');
415                              $cur->comment_author = $name;
416                              $cur->comment_site = html::clean($site);
417                              $cur->comment_email = html::clean($mail);
418                              $cur->comment_content = $content;
419                              $cur->post_id = $_ctx->posts->post_id;
420                              $cur->comment_status = $core->blog->settings->system->comments_pub ? 1 : -1;
421                              $cur->comment_ip = http::realIP();
422                             
423                              $redir = $_ctx->posts->getURL();
424                              $redir .= $core->blog->settings->system->url_scan == 'query_string' ? '&' : '?';
425                             
426                              try
427                              {
428                                   if (!text::isEmail($cur->comment_email)) {
429                                        throw new Exception(__('You must provide a valid email address.'));
430                                   }
431                                   
432                                   # --BEHAVIOR-- publicBeforeCommentCreate
433                                   $core->callBehavior('publicBeforeCommentCreate',$cur);
434                                   if ($cur->post_id) {                         
435                                        $comment_id = $core->blog->addComment($cur);
436                                       
437                                        # --BEHAVIOR-- publicAfterCommentCreate
438                                        $core->callBehavior('publicAfterCommentCreate',$cur,$comment_id);
439                                   }
440                                   
441                                   if ($cur->comment_status == 1) {
442                                        $redir_arg = 'pub=1';
443                                   } else {
444                                        $redir_arg = 'pub=0';
445                                   }
446                                   
447                                   header('Location: '.$redir.$redir_arg);
448                              }
449                              catch (Exception $e)
450                              {
451                                   $_ctx->form_error = $e->getMessage();
452                                   $_ctx->form_error;
453                              }
454                         }
455                    }
456                   
457                    # The entry
458                    self::serveDocument('post.html');
459               }
460          }
461     }
462     
463     public static function preview($args)
464     {
465          $core = $GLOBALS['core'];
466          $_ctx = $GLOBALS['_ctx'];
467         
468          if (!preg_match('#^(.+?)/([0-9a-z]{40})/(.+?)$#',$args,$m)) {
469               # The specified Preview URL is malformed.
470               self::p404();
471          }
472          else
473          {
474               $user_id = $m[1];
475               $user_key = $m[2];
476               $post_url = $m[3];
477               if (!$core->auth->checkUser($user_id,null,$user_key)) {
478                    # The user has no access to the entry.
479                    self::p404();
480               }
481               else
482               {
483                    $_ctx->preview = true;
484                    self::post($post_url);
485               }
486          }
487     }
488     
489     public static function feed($args)
490     {
491          $type = null;
492          $comments = false;
493          $cat_url = false;
494          $post_id = null;
495          $subtitle = '';
496         
497          $mime = 'application/xml';
498         
499          $_ctx =& $GLOBALS['_ctx'];
500          $core =& $GLOBALS['core'];
501         
502          if (preg_match('!^([a-z]{2}(-[a-z]{2})?)/(.*)$!',$args,$m)) {
503               $params = new ArrayObject(array('lang' => $m[1]));
504               
505               $args = $m[3];
506               
507               $core->callBehavior('publicFeedBeforeGetLangs',$params,$args);
508               
509               $_ctx->langs = $core->blog->getLangs($params);
510               
511               if ($_ctx->langs->isEmpty()) {
512                    # The specified language does not exist.
513                    self::p404();
514                    return;
515               } else {
516                    $_ctx->cur_lang = $m[1];
517               }
518          }
519         
520          if (preg_match('#^rss2/xslt$#',$args,$m))
521          {
522               # RSS XSLT stylesheet
523               self::serveDocument('rss2.xsl','text/xml');
524               return;
525          }
526          elseif (preg_match('#^(atom|rss2)/comments/([0-9]+)$#',$args,$m))
527          {
528               # Post comments feed
529               $type = $m[1];
530               $comments = true;
531               $post_id = (integer) $m[2];
532          }
533          elseif (preg_match('#^(?:category/(.+)/)?(atom|rss2)(/comments)?$#',$args,$m))
534          {
535               # All posts or comments feed
536               $type = $m[2];
537               $comments = !empty($m[3]);
538               if (!empty($m[1])) {
539                    $cat_url = $m[1];
540               }
541          }
542          else
543          {
544               # The specified Feed URL is malformed.
545               self::p404();
546               return;
547          }
548         
549          if ($cat_url)
550          {
551               $params = new ArrayObject(array(
552                    'cat_url' => $cat_url,
553                    'post_type' => 'post'));
554               
555               $core->callBehavior('publicFeedBeforeGetCategories',$params,$args);
556               
557               $_ctx->categories = $core->blog->getCategories($params);
558               
559               if ($_ctx->categories->isEmpty()) {
560                    # The specified category does no exist.
561                    self::p404();
562                    return;
563               }
564               
565               $subtitle = ' - '.$_ctx->categories->cat_title;
566          }
567          elseif ($post_id)
568          {
569               $params = new ArrayObject(array(
570                    'post_id' => $post_id,
571                    'post_type' => ''));
572                   
573               $core->callBehavior('publicFeedBeforeGetPosts',$params,$args);
574               
575               $_ctx->posts = $core->blog->getPosts($params);
576               
577               if ($_ctx->posts->isEmpty()) {
578                    # The specified post does not exist.
579                    self::p404();
580                    return;
581               }
582               
583               $subtitle = ' - '.$_ctx->posts->post_title;
584          }
585         
586          $tpl = $type;
587          if ($comments) {
588               $tpl .= '-comments';
589               $_ctx->nb_comment_per_page = $core->blog->settings->system->nb_comment_per_feed;
590          } else {
591               $_ctx->nb_entry_per_page = $core->blog->settings->system->nb_post_per_feed;
592               $_ctx->short_feed_items = $core->blog->settings->system->short_feed_items;
593          }
594          $tpl .= '.xml';
595         
596          if ($type == 'atom') {
597               $mime = 'application/atom+xml';
598          }
599         
600          $_ctx->feed_subtitle = $subtitle;
601         
602          header('X-Robots-Tag: '.context::robotsPolicy($core->blog->settings->system->robots_policy,''));
603          self::serveDocument($tpl,$mime);
604          if (!$comments && !$cat_url) {
605               $core->blog->publishScheduledEntries();
606          }
607     }
608     
609     public static function trackback($args)
610     {
611          if (!preg_match('/^[0-9]+$/',$args)) {
612               # The specified trackback URL is not an number
613               self::p404();
614          } else {
615               $tb = new dcTrackback($GLOBALS['core']);
616               $tb->receive($args);
617          }
618     }
619     
620     public static function rsd($args)
621     {
622          $core =& $GLOBALS['core'];
623          http::cache($GLOBALS['mod_files'],$GLOBALS['mod_ts']);
624         
625          header('Content-Type: text/xml; charset=UTF-8');
626          echo
627          '<?xml version="1.0" encoding="UTF-8"?>'."\n".
628          '<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">'."\n".
629          "<service>\n".
630          "  <engineName>Dotclear</engineName>\n".
631          "  <engineLink>http://www.dotclear.org/</engineLink>\n".
632          '  <homePageLink>'.html::escapeHTML($core->blog->url)."</homePageLink>\n";
633         
634          if ($core->blog->settings->system->enable_xmlrpc)
635          {
636               $u = sprintf(DC_XMLRPC_URL,$core->blog->url,$core->blog->id);
637               
638               echo
639               "  <apis>\n".
640               '    <api name="WordPress" blogID="1" preferred="true" apiLink="'.$u.'"/>'."\n".
641               '    <api name="Movable Type" blogID="1" preferred="false" apiLink="'.$u.'"/>'."\n".
642               '    <api name="MetaWeblog" blogID="1" preferred="false" apiLink="'.$u.'"/>'."\n".
643               '    <api name="Blogger" blogID="1" preferred="false" apiLink="'.$u.'"/>'."\n".
644               "  </apis>\n";
645          }
646         
647          echo
648          "</service>\n".
649          "</rsd>\n";
650     }
651     
652     public static function xmlrpc($args)
653     {
654          $core =& $GLOBALS['core'];
655          $blog_id = preg_replace('#^([^/]*).*#','$1',$args);
656          $server = new dcXmlRpc($core,$blog_id);
657          $server->serve();
658     }
659}
660?>
Note: See TracBrowser for help on using the repository browser.

Sites map