Dotclear

source: inc/public/lib.urlhandlers.php @ 1674:00146501e490

Revision 1674:00146501e490, 16.4 KB checked in by Florent Cotton <florent.cotton@…>, 12 years ago (diff)

Support des pingbacks : après le support de la détection et de l'envoi de pingbacks, au tour du support en réception.
Dans le détail (ou presque) :

  • Ajout d'un endpoint "pingback.ping" XML-RPC, mais le tout le gros du traitement est dans le fichier class.dc.trackback.php
  • Ajout d'une méthode "receive_pb" dans la classe dcTrackback pour la prise en charge quasi-complète de la réception et enregistrement d'un pingback.
  • Ajout d'une balise template {{tpl:BlogXMLRPCURL}} pour retourner l'URL du serveur XML-RPC du blog courant
  • Ajout d'un bloc au niveau des en-têtes dans les templates par défaut post.html et page.html pour la mise en oeuvre si besoin d'un <link rel="pingback" ../>
  • Ajout de l'envoi d'un en-tête HTTP supplémentaire "X-Pingback" dans les gestionnaires d'URLs pour les types "post" et "pages"

Reste plus qu'à tester en conditions réelles et à polir au besoin.

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

Sites map