3 /* Copyright (C) 2004-2011 $ThePhpWikiProgrammingTeam
5 * This file is part of PhpWiki.
7 * PhpWiki is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * PhpWiki is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with PhpWiki; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 // display.php: fetch page or get default content
24 require_once('lib/Template.php');
27 * Extract keywords from Category* links on page.
29 function GleanKeywords ($page) {
30 if (!defined('KEYWORDS')) return '';
31 require_once("lib/TextSearchQuery.php");
32 $search = new TextSearchQuery(KEYWORDS, true);
33 $KeywordLinkRegexp = $search->asRegexp();
34 // iterate over the pagelinks (could be a large number) [15ms on PluginManager]
35 // or do a titleSearch and check the categories if they are linked?
36 $links = $page->getPageLinks();
37 $keywords[] = SplitPagename($page->getName());
38 while ($link = $links->next()) {
40 if (preg_match($KeywordLinkRegexp, $link->getName(), $m))
41 $keywords[] = SplitPagename($m[0]);
43 $keywords[] = WIKI_NAME;
44 return join(', ', $keywords);
47 /** Make a link back to redirecting page.
49 * @param $pagename string Name of redirecting page.
50 * @return XmlContent Link to the redirecting page.
52 function RedirectorLink($pagename) {
53 $url = WikiURL($pagename, array('redirectfrom' => ''));
54 return HTML::a(array('class' => 'redirectfrom wiki',
59 /* only on ?action= */
60 function actionPage(&$request, $action) {
64 $pagename = $request->getArg('pagename');
65 $version = $request->getArg('version');
67 $page = $request->getPage();
68 $revision = $page->getCurrentRevision();
70 $dbi = $request->getDbh();
71 $actionpage = $dbi->getPage($action);
72 $actionrev = $actionpage->getCurrentRevision();
74 $pagetitle = HTML(fmt("%s: %s",
75 $actionpage->getName(),
76 $WikiTheme->linkExistingWikiWord($pagename, false, $version)));
78 $request->setValidators(array('pageversion' => $revision->getVersion(),
79 '%mtime' => $revision->get('mtime')));
80 $request->appendValidators(array('pagerev' => $revision->getVersion(),
81 '%mtime' => $revision->get('mtime')));
82 $request->appendValidators(array('actionpagerev' => $actionrev->getVersion(),
83 '%mtime' => $actionrev->get('mtime')));
85 $transformedContent = $actionrev->getTransformedContent();
87 /* Optionally tell google (and others) not to take notice of action pages.
88 RecentChanges or AllPages might be an exception.
91 if (GOOGLE_LINKS_NOFOLLOW) {
92 $robots = "noindex,nofollow";
93 $args = array('ROBOTS_META' => $robots);
96 /* Handle other formats: So far we had html only.
97 xml is requested by loaddump, rss is handled by recentchanges,
98 pdf is a special action, but should be a format to dump multiple pages
99 if the actionpage plugin returns a pagelist.
100 rdf and owl are handled by SemanticWeb.
102 $format = $request->getArg('format');
104 /* At first the single page formats: html, xml */
105 if ($pagename == _("LinkDatabase")) {
106 $template = Template('browse', array('CONTENT' => $transformedContent));
107 GeneratePage($template, $pagetitle, $revision, $args);
108 } elseif (!$format or $format == 'html' or $format == 'sidebar' or $format == 'contribs') {
109 $template = Template('browse', array('CONTENT' => $transformedContent));
110 GeneratePage($template, $pagetitle, $revision, $args);
111 } elseif ($format == 'xml') {
112 $request->setArg('format','');
113 $template = new Template('browse', $request,
114 array('revision' => $revision,
115 'CONTENT' => $transformedContent,
117 $html = GeneratePageAsXML($template, $pagename, $revision /*,
118 array('VALID_LINKS' => $args['VALID_LINKS'])*/);
119 header("Content-Type: application/xhtml+xml; charset=" . $GLOBALS['charset']);
123 require_once('lib/WikiPlugin.php');
124 // Then the multi-page formats
125 // rss (if not already handled by RecentChanges)
126 // Need the pagelist from the first plugin
127 foreach($transformedContent->_content as $cached_element) {
128 if (is_a($cached_element, "Cached_PluginInvocation")) {
129 $loader = new WikiPluginLoader;
131 // return the first found pagelist
132 $pagelist = $loader->expandPI($cached_element->_pi, $request,
134 if (is_a($pagelist, 'PageList'))
138 if (!$pagelist or !is_a($pagelist, 'PageList')) {
139 if (!in_array($format, array("rss91","rss2","rss","atom","rdf")))
140 trigger_error(sprintf("Format %s requires an actionpage returning a pagelist.",
142 ."\n".("Fall back to single page mode"), E_USER_WARNING);
143 require_once('lib/PageList.php');
144 $pagelist = new PageList();
145 if ($format == 'pdf')
146 $pagelist->addPage($page);
148 foreach ($pagelist->_pages as $page) {
149 $name = $page->getName();
150 if ($name != $pagename and $page->exists())
151 $args['VALID_LINKS'][] = $name;
154 if ($format == 'pdf') {
155 require_once("lib/pdf.php");
156 array_unshift($args['VALID_LINKS'], $pagename);
157 ConvertAndDisplayPdfPageList($request, $pagelist, $args);
159 elseif ($format == 'ziphtml') { // need to fix links
160 require_once('lib/loadsave.php');
161 array_unshift($args['VALID_LINKS'], $pagename);
162 $request->setArg('zipname', FilenameForPage($pagename).".zip");
163 $request->setArg('pages', $args['VALID_LINKS']);
164 $request->setArg('format','');
165 MakeWikiZipHtml($request);
166 } // time-sorted RDF à la RecentChanges
167 elseif (in_array($format, array("rss91","rss2","rss","atom"))) {
168 $args = $request->getArgs();
169 //$request->setArg('format','');
170 if ($pagename == _("RecentChanges")) {
171 $template->printExpansion($args);
173 require_once("lib/plugin/RecentChanges.php");
174 $plugin = new WikiPlugin_RecentChanges();
175 return $plugin->format($plugin->getChanges($request->_dbi, $args), $args);
177 } elseif ($format == 'json') { // for faster autocompletion on searches
178 $req_args =& $request->args;
179 unset($req_args['format']);
180 $json = array('count' => count($pagelist->_pages),
181 'list' => $args['VALID_LINKS'],
183 'phpwiki-version' => PHPWIKI_VERSION);
184 if (loadPhpExtension('json')) {
185 $json_enc = json_encode($json);
187 require_once("lib/pear/JSON.php");
188 $j = new Services_JSON();
189 $json_enc = $j->encode($json);
191 header("Content-Type: application/json");
193 } elseif ($format == 'rdf') { // all semantic relations and attributes
194 require_once("lib/SemanticWeb.php");
195 $rdf = new RdfWriter($request, $pagelist);
197 } elseif ($format == 'rdfs') {
198 require_once("lib/SemanticWeb.php");
199 $rdf = new RdfsWriter($request, $pagelist);
201 } elseif ($format == 'owl') { // or daml?
202 require_once("lib/SemanticWeb.php");
203 $rdf = new OwlWriter($request, $pagelist);
206 if (!in_array($pagename, array(_("LinkDatabase"))))
207 trigger_error(sprintf(_("Unsupported argument: %s=%s"),"format",$format),
209 $template = Template('browse', array('CONTENT' => $transformedContent));
210 GeneratePage($template, $pagetitle, $revision, $args);
213 $request->checkValidators();
218 function displayPage(&$request, $template=false) {
221 $pagename = $request->getArg('pagename');
222 $version = $request->getArg('version');
223 $page = $request->getPage();
226 $revision = $page->getRevision($version);
228 NoSuchRevision($request, $page, $version);
229 /* Tell Google (and others) to ignore old versions of pages */
230 $robots = "noindex,nofollow";
231 $toks['ROBOTS_META'] = $robots;
233 $revision = $page->getCurrentRevision();
235 $format = $request->getArg('format');
236 if ($format == 'xml') { // fast ajax: include page content asynchronously
238 header("Content-Type: text/xml");
239 echo "<","?xml version=\"1.0\" encoding=\"$charset\"?", ">\n";
240 // DOCTYPE html needed to allow unencoded entities like without !CDATA[]
241 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
242 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',"\n";
243 if ($page->exists()) {
244 header("Last-Modified: " . Rfc1123DateTime($revision->get('mtime')));
245 $request->cacheControl();
246 $request->setArg('format','');
247 $page_content = $revision->getTransformedContent();
248 $page_content->printXML();
249 $request->_is_buffering_output = false; // avoid wrong Content-Length with errors
252 $request->cacheControl();
253 echo('<div style="display:none;" />');
254 $request->_is_buffering_output = false; // avoid wrong Content-Length with errors
260 if (isSubPage($pagename)) {
261 $pages = explode(SUBPAGE_SEPARATOR, $pagename);
262 $last_page = array_pop($pages); // deletes last element from array as side-effect
263 $pageheader = HTML::span(HTML::a(array('href' => WikiURL($pages[0]),
264 'class' => 'pagetitle'
266 $WikiTheme->maybeSplitWikiWord($pages[0] . SUBPAGE_SEPARATOR)));
267 $first_pages = $pages[0] . SUBPAGE_SEPARATOR;
269 foreach ($pages as $p) {
270 $pageheader->pushContent(HTML::a(array('href' => WikiURL($first_pages . $p),
271 'class' => 'backlinks'),
272 $WikiTheme->maybeSplitWikiWord($p . SUBPAGE_SEPARATOR)));
273 $first_pages .= $p . SUBPAGE_SEPARATOR;
275 $backlink = HTML::a(array('href' => WikiURL($pagename,
276 array('action' => _("BackLinks"))),
277 'class' => 'backlinks'),
278 $WikiTheme->maybeSplitWikiWord($last_page));
279 $backlink->addTooltip(sprintf(_("BackLinks for %s"), $pagename));
280 $pageheader->pushContent($backlink);
282 $pageheader = HTML::a(array('href' => WikiURL($pagename,
283 array('action' => _("BackLinks"))),
284 'class' => 'backlinks'),
285 $WikiTheme->maybeSplitWikiWord($pagename));
286 $pageheader->addTooltip(sprintf(_("BackLinks for %s"), $pagename));
287 if ($request->getArg('frame'))
288 $pageheader->setAttr('target', '_top');
291 $pagetitle = SplitPagename($pagename);
292 if (($redirect_from = $request->getArg('redirectfrom'))) {
293 $redirect_message = HTML::span(array('class' => 'redirectfrom'),
294 fmt("(Redirected from %s)",
295 RedirectorLink($redirect_from)));
296 // abuse the $redirected template var for some status update notice
297 } elseif ($request->getArg('errormsg')) {
298 $redirect_message = $request->getArg('errormsg');
299 $request->setArg('errormsg', false);
302 $request->appendValidators(array('pagerev' => $revision->getVersion(),
303 '%mtime' => $revision->get('mtime')));
305 // FIXME: This is also in the template...
306 if ($request->getArg('action') != 'pdf' and !headers_sent()) {
307 // FIXME: enable MathML/SVG/... support
310 and strstr($request->get('HTTP_ACCEPT'),'application/xhtml+xml')))
311 header("Content-Type: application/xhtml+xml; charset=" . $GLOBALS['charset']);
313 header("Content-Type: text/html; charset=" . $GLOBALS['charset']);
317 $toks['TITLE'] = $pagetitle; // <title> tag
318 $toks['HEADER'] = $pageheader; // h1 with backlink
319 $toks['revision'] = $revision;
321 // On external searchengine (google) referrer, highlight the searchterm and
322 // pass through the Searchhighlight actionpage.
323 if ($result = isExternalReferrer($request)) {
324 if (!empty($result['query'])) {
325 if (ENABLE_SEARCHHIGHLIGHT) {
326 $request->_searchhighlight = $result;
327 $request->appendValidators(array('%mtime' => time())); // force no cache(?)
328 // Should be changed to check the engine and search term only
329 // $request->setArg('nocache', 1);
330 $page_content = new TransformedText($revision->getPage(),
331 $revision->getPackedContent(),
332 $revision->getMetaData());
333 /* Now add the SearchHighlight plugin to the top of the page, in memory only.
334 You can parametrize this by changing the SearchHighlight action page.
336 if ($actionpage = $request->findActionPage('SearchHighlight')) {
337 $actionpage = $request->getPage($actionpage);
338 $actionrev = $actionpage->getCurrentRevision();
339 $pagetitle = HTML(fmt("%s: %s",
340 $actionpage->getName(),
341 $WikiTheme->linkExistingWikiWord($pagename, false, $version)));
342 $request->appendValidators(array('actionpagerev' => $actionrev->getVersion(),
343 '%mtime' => $actionrev->get('mtime')));
344 $toks['SEARCH_ENGINE'] = $result['engine'];
345 $toks['SEARCH_ENGINE_URL'] = $result['engine_url'];
346 $toks['SEARCH_TERM'] = $result['query'];
347 //$toks['HEADER'] = HTML($actionpage->getName(),": ",$pageheader); // h1 with backlink
348 $actioncontent = new TransformedText($actionrev->getPage(),
349 $actionrev->getPackedContent(),
350 $actionrev->getMetaData());
351 // prepend the actionpage in front of the hightlighted content
352 $toks['CONTENT'] = HTML($actioncontent, $page_content);
356 $page_content = $revision->getTransformedContent();
359 $page_content = $revision->getTransformedContent();
362 if ($pagename == _("SandBox")) {
363 $robots = "noindex,nofollow";
364 $toks['ROBOTS_META'] = $robots;
365 } else if (isActionPage($pagename)) {
366 $robots = "noindex,nofollow";
367 $toks['ROBOTS_META'] = $robots;
368 } else if (!isset($toks['ROBOTS_META'])) {
369 $robots = "index,follow";
370 $toks['ROBOTS_META'] = $robots;
372 if (!isset($toks['CONTENT']))
373 $toks['CONTENT'] = new Template('browse', $request, $page_content);
374 if (!empty($redirect_message))
375 $toks['redirected'] = $redirect_message;
377 // Massive performance problem parsing at run-time into all xml objects
378 // looking for p's. Should be optional, if not removed at all.
379 //$toks['PAGE_DESCRIPTION'] = $page_content->getDescription();
380 $toks['PAGE_KEYWORDS'] = GleanKeywords($page);
382 $template = new Template('html', $request);
384 // Handle other formats: So far we had html only.
385 // xml is requested by loaddump, rss is handled by RecentChanges,
386 // pdf is a special action, but should be a format to dump multiple pages
387 // if the actionpage plugin returns a pagelist.
388 // rdf, owl, kbmodel, daml, ... are handled by SemanticWeb.
389 /* Only single page versions. rss only if not already handled by RecentChanges.
391 if (!$format or $format == 'html' or $format == 'sidebar' or $format == 'contribs') {
392 $template->printExpansion($toks);
394 // No pagelist here. Single page version only
395 require_once("lib/PageList.php");
396 $pagelist = new PageList();
397 $pagelist->addPage($page);
398 if ($format == 'pdf') {
399 require_once("lib/pdf.php");
400 $request->setArg('format','');
401 ConvertAndDisplayPdfPageList($request, $pagelist);
402 // time-sorted rdf a la RecentChanges
403 } elseif (in_array($format, array("rss91","rss2","rss","atom"))) {
404 //$request->setArg('format','');
405 if ($pagename == _("RecentChanges"))
406 $template->printExpansion($toks);
408 require_once("lib/plugin/RecentChanges.php");
409 $plugin = new WikiPlugin_RecentChanges();
410 $args = $request->getArgs();
411 return $plugin->format($plugin->getChanges($request->_dbi, $args), $args);
413 } elseif ($format == 'rdf') { // all semantic relations and attributes
414 require_once("lib/SemanticWeb.php");
415 $rdf = new RdfWriter($request, $pagelist);
417 } elseif ($format == 'owl') { // or daml?
418 require_once("lib/SemanticWeb.php");
419 $rdf = new OwlWriter($request, $pagelist);
421 } elseif ($format == 'json') { // include page content asynchronously
422 $request->setArg('format','');
424 $content = $page_content->asXML();
427 $req_args = $request->args;
428 unset($req_args['format']);
429 // no meta-data so far, just the content
430 $json = array('content' => $content,
432 'phpwiki-version' => PHPWIKI_VERSION);
433 if (loadPhpExtension('json')) {
434 $json_enc = json_encode($json);
436 require_once("lib/pear/JSON.php");
437 $j = new Services_JSON();
438 $json_enc = $j->encode($json);
440 header("Content-Type: application/json");
443 if (!in_array($pagename, array(_("LinkDatabase"))))
444 trigger_error(sprintf(_("Unsupported argument: %s=%s"),"format",$format),
446 $template->printExpansion($toks);
450 $page->increaseHitCount();
452 if ($request->getArg('action') != 'pdf') {
453 $request->checkValidators();
463 // c-hanging-comment-ender-p: nil
464 // indent-tabs-mode: nil