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)
31 if (!defined('KEYWORDS')) return '';
32 require_once 'lib/TextSearchQuery.php';
33 $search = new TextSearchQuery(KEYWORDS, true);
34 $KeywordLinkRegexp = $search->asRegexp();
35 // iterate over the pagelinks (could be a large number) [15ms on PluginManager]
36 // or do a titleSearch and check the categories if they are linked?
37 $links = $page->getPageLinks();
38 $keywords[] = SplitPagename($page->getName());
39 while ($link = $links->next()) {
41 if (preg_match($KeywordLinkRegexp, $link->getName(), $m))
42 $keywords[] = SplitPagename($m[0]);
44 $keywords[] = WIKI_NAME;
45 return join(', ', $keywords);
48 /** Make a link back to redirecting page.
50 * @param $pagename string Name of redirecting page.
51 * @return XmlContent Link to the redirecting page.
53 function RedirectorLink($pagename)
55 $url = WikiURL($pagename, array('redirectfrom' => ''));
56 return HTML::a(array('class' => 'redirectfrom wiki',
61 /* only on ?action= */
62 function actionPage(&$request, $action)
67 $pagename = $request->getArg('pagename');
68 $version = $request->getArg('version');
70 $page = $request->getPage();
71 $revision = $page->getCurrentRevision();
73 $dbi = $request->getDbh();
74 $actionpage = $dbi->getPage($action);
75 $actionrev = $actionpage->getCurrentRevision();
77 $pagetitle = HTML(fmt("%s: %s",
78 $actionpage->getName(),
79 $WikiTheme->linkExistingWikiWord($pagename, false, $version)));
81 $request->setValidators(array('pageversion' => $revision->getVersion(),
82 '%mtime' => $revision->get('mtime')));
83 $request->appendValidators(array('pagerev' => $revision->getVersion(),
84 '%mtime' => $revision->get('mtime')));
85 $request->appendValidators(array('actionpagerev' => $actionrev->getVersion(),
86 '%mtime' => $actionrev->get('mtime')));
88 $transformedContent = $actionrev->getTransformedContent();
90 /* Optionally tell google (and others) not to take notice of action pages.
91 RecentChanges or AllPages might be an exception.
94 if (GOOGLE_LINKS_NOFOLLOW) {
95 $robots = "noindex,nofollow";
96 $args = array('ROBOTS_META' => $robots);
99 /* Handle other formats: So far we had html only.
100 xml is requested by loaddump, rss is handled by recentchanges,
101 pdf is a special action, but should be a format to dump multiple pages
102 if the actionpage plugin returns a pagelist.
103 rdf and owl are handled by SemanticWeb.
105 $format = $request->getArg('format');
107 /* At first the single page formats: html, xml */
108 if ($pagename == _("LinkDatabase")) {
109 $template = Template('browse', array('CONTENT' => $transformedContent));
110 GeneratePage($template, $pagetitle, $revision, $args);
111 } elseif (!$format or $format == 'html' or $format == 'sidebar' or $format == 'contribs') {
112 $template = Template('browse', array('CONTENT' => $transformedContent));
113 GeneratePage($template, $pagetitle, $revision, $args);
114 } elseif ($format == 'xml') {
115 $request->setArg('format', '');
116 $template = new Template('browse', $request,
117 array('revision' => $revision,
118 'CONTENT' => $transformedContent,
120 $html = GeneratePageAsXML($template, $pagename, $revision /*,
121 array('VALID_LINKS' => $args['VALID_LINKS'])*/);
122 header("Content-Type: application/xhtml+xml; charset=UTF-8");
126 require_once 'lib/WikiPlugin.php';
127 // Then the multi-page formats
128 // rss (if not already handled by RecentChanges)
129 // Need the pagelist from the first plugin
130 foreach ($transformedContent->_content as $cached_element) {
131 if (is_a($cached_element, "Cached_PluginInvocation")) {
132 $loader = new WikiPluginLoader();
134 // return the first found pagelist
135 $pagelist = $loader->expandPI($cached_element->_pi, $request,
137 if (is_a($pagelist, 'PageList'))
141 if (!$pagelist or !is_a($pagelist, 'PageList')) {
142 if (!in_array($format, array("rss91", "rss2", "rss", "atom", "rdf")))
143 trigger_error(sprintf("Format %s requires an actionpage returning a pagelist.",
145 . "\n" . ("Fall back to single page mode"), E_USER_WARNING);
146 require_once 'lib/PageList.php';
147 $pagelist = new PageList();
148 if ($format == 'pdf')
149 $pagelist->addPage($page);
151 foreach ($pagelist->_pages as $page) {
152 $name = $page->getName();
153 if ($name != $pagename and $page->exists())
154 $args['VALID_LINKS'][] = $name;
157 if ($format == 'pdf') {
158 require_once 'lib/pdf.php';
159 array_unshift($args['VALID_LINKS'], $pagename);
160 ConvertAndDisplayPdfPageList($request, $pagelist, $args);
161 } elseif ($format == 'ziphtml') { // need to fix links
162 require_once 'lib/loadsave.php';
163 array_unshift($args['VALID_LINKS'], $pagename);
164 $request->setArg('zipname', FilenameForPage($pagename) . ".zip");
165 $request->setArg('pages', $args['VALID_LINKS']);
166 $request->setArg('format', '');
167 MakeWikiZipHtml($request);
168 } // time-sorted RDF à la RecentChanges
169 elseif (in_array($format, array("rss91", "rss2", "rss", "atom"))) {
170 $args = $request->getArgs();
171 //$request->setArg('format','');
172 if ($pagename == _("RecentChanges")) {
173 $template->printExpansion($args);
175 require_once 'lib/plugin/RecentChanges.php';
176 $plugin = new WikiPlugin_RecentChanges();
177 return $plugin->format($plugin->getChanges($request->_dbi, $args), $args);
179 } elseif ($format == 'json') { // for faster autocompletion on searches
180 $req_args =& $request->args;
181 unset($req_args['format']);
182 $json = array('count' => count($pagelist->_pages),
183 'list' => $args['VALID_LINKS'],
185 'phpwiki-version' => PHPWIKI_VERSION);
186 if (loadPhpExtension('json')) {
187 $json_enc = json_encode($json);
189 require_once 'lib/pear/JSON.php';
190 $j = new Services_JSON();
191 $json_enc = $j->encode($json);
193 header("Content-Type: application/json");
195 } elseif ($format == 'rdf') { // all semantic relations and attributes
196 require_once 'lib/SemanticWeb.php';
197 $rdf = new RdfWriter($request, $pagelist);
199 } elseif ($format == 'rdfs') {
200 require_once 'lib/SemanticWeb.php';
201 $rdf = new RdfsWriter($request, $pagelist);
203 } elseif ($format == 'owl') { // or daml?
204 require_once 'lib/SemanticWeb.php';
205 $rdf = new OwlWriter($request, $pagelist);
208 if (!in_array($pagename, array(_("LinkDatabase"))))
209 trigger_error(sprintf(_("Unsupported argument: %s=%s"), "format", $format),
211 $template = Template('browse', array('CONTENT' => $transformedContent));
212 GeneratePage($template, $pagetitle, $revision, $args);
215 $request->checkValidators();
220 function displayPage(&$request, $template = false)
224 $pagename = $request->getArg('pagename');
225 $version = $request->getArg('version');
226 $page = $request->getPage();
229 $revision = $page->getRevision($version);
231 NoSuchRevision($request, $page, $version);
232 /* Tell Google (and others) to ignore old versions of pages */
233 $robots = "noindex,nofollow";
234 $toks['ROBOTS_META'] = $robots;
236 $revision = $page->getCurrentRevision();
238 $format = $request->getArg('format');
239 if ($format == 'xml') { // fast ajax: include page content asynchronously
240 header("Content-Type: text/xml");
241 echo "<", "?xml version=\"1.0\" encoding=\"UTF-8\"?", ">\n";
242 // DOCTYPE html needed to allow unencoded entities like without !CDATA[]
243 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
244 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">', "\n";
245 if ($page->exists()) {
246 header("Last-Modified: " . Rfc1123DateTime($revision->get('mtime')));
247 $request->cacheControl();
248 $request->setArg('format', '');
249 $page_content = $revision->getTransformedContent();
250 $page_content->printXML();
251 $request->_is_buffering_output = false; // avoid wrong Content-Length with errors
254 $request->cacheControl();
255 echo('<div style="display:none;" />');
256 $request->_is_buffering_output = false; // avoid wrong Content-Length with errors
262 if (isSubPage($pagename)) {
263 $pages = explode(SUBPAGE_SEPARATOR, $pagename);
264 $last_page = array_pop($pages); // deletes last element from array as side-effect
265 $pageheader = HTML::span(HTML::a(array('href' => WikiURL($pages[0]),
266 'class' => 'pagetitle'
268 $WikiTheme->maybeSplitWikiWord($pages[0] . SUBPAGE_SEPARATOR)));
269 $first_pages = $pages[0] . SUBPAGE_SEPARATOR;
271 foreach ($pages as $p) {
272 $pageheader->pushContent(HTML::a(array('href' => WikiURL($first_pages . $p),
273 'class' => 'backlinks'),
274 $WikiTheme->maybeSplitWikiWord($p . SUBPAGE_SEPARATOR)));
275 $first_pages .= $p . SUBPAGE_SEPARATOR;
277 $backlink = HTML::a(array('href' => WikiURL($pagename,
278 array('action' => __("BackLinks"))),
279 'class' => 'backlinks'),
280 $WikiTheme->maybeSplitWikiWord($last_page));
281 $backlink->addTooltip(sprintf(_("BackLinks for %s"), $pagename));
282 $pageheader->pushContent($backlink);
284 $pageheader = HTML::a(array('href' => WikiURL($pagename,
285 array('action' => __("BackLinks"))),
286 'class' => 'backlinks'),
287 $WikiTheme->maybeSplitWikiWord($pagename));
288 $pageheader->addTooltip(sprintf(_("BackLinks for %s"), $pagename));
289 if ($request->getArg('frame'))
290 $pageheader->setAttr('target', '_top');
293 $pagetitle = SplitPagename($pagename);
294 if (($redirect_from = $request->getArg('redirectfrom'))) {
295 $redirect_message = HTML::span(array('class' => 'redirectfrom'),
296 fmt("(Redirected from %s)",
297 RedirectorLink($redirect_from)));
298 // abuse the $redirected template var for some status update notice
299 } elseif ($request->getArg('errormsg')) {
300 $redirect_message = HTML::p(array('class' => 'error'),
301 $request->getArg('errormsg'));
302 $request->setArg('errormsg', false);
303 } elseif ($request->getArg('warningmsg')) {
304 $redirect_message = HTML::p(array('class' => 'warning_msg'),
305 $request->getArg('warningmsg'));
306 $request->setArg('errormsg', false);
309 $request->appendValidators(array('pagerev' => $revision->getVersion(),
310 '%mtime' => $revision->get('mtime')));
312 $toks['TITLE'] = $pagetitle; // <title> tag
313 $toks['HEADER'] = $pageheader; // h1 with backlink
314 $toks['revision'] = $revision;
316 // On external searchengine (google) referrer, highlight the searchterm and
317 // pass through the Searchhighlight actionpage.
318 if ($result = isExternalReferrer($request)) {
319 if (!empty($result['query'])) {
320 if (ENABLE_SEARCHHIGHLIGHT) {
321 $request->_searchhighlight = $result;
322 $request->appendValidators(array('%mtime' => time())); // force no cache(?)
323 // Should be changed to check the engine and search term only
324 // $request->setArg('nocache', 1);
325 $page_content = new TransformedText($revision->getPage(),
326 $revision->getPackedContent(),
327 $revision->getMetaData());
328 /* Now add the SearchHighlight plugin to the top of the page, in memory only.
329 You can parametrize this by changing the SearchHighlight action page.
331 if ($actionpage = $request->findActionPage('SearchHighlight')) {
332 $actionpage = $request->getPage($actionpage);
333 $actionrev = $actionpage->getCurrentRevision();
334 $pagetitle = HTML(fmt("%s: %s",
335 $actionpage->getName(),
336 $WikiTheme->linkExistingWikiWord($pagename, false, $version)));
337 $request->appendValidators(array('actionpagerev' => $actionrev->getVersion(),
338 '%mtime' => $actionrev->get('mtime')));
339 $toks['SEARCH_ENGINE'] = $result['engine'];
340 $toks['SEARCH_ENGINE_URL'] = $result['engine_url'];
341 $toks['SEARCH_TERM'] = $result['query'];
342 //$toks['HEADER'] = HTML($actionpage->getName(),": ",$pageheader); // h1 with backlink
343 $actioncontent = new TransformedText($actionrev->getPage(),
344 $actionrev->getPackedContent(),
345 $actionrev->getMetaData());
346 // prepend the actionpage in front of the hightlighted content
347 $toks['CONTENT'] = HTML($actioncontent, $page_content);
351 $page_content = $revision->getTransformedContent();
354 $page_content = $revision->getTransformedContent();
357 if ($pagename == _("SandBox")) {
358 $robots = "noindex,nofollow";
359 $toks['ROBOTS_META'] = $robots;
360 } elseif (isActionPage($pagename)) {
361 $robots = "noindex,nofollow";
362 $toks['ROBOTS_META'] = $robots;
363 } elseif (!isset($toks['ROBOTS_META'])) {
364 $robots = "index,follow";
365 $toks['ROBOTS_META'] = $robots;
367 if (!isset($toks['CONTENT']))
368 $toks['CONTENT'] = new Template('browse', $request, $page_content);
369 if (!empty($redirect_message))
370 $toks['redirected'] = $redirect_message;
372 // Massive performance problem parsing at run-time into all xml objects
373 // looking for p's. Should be optional, if not removed at all.
374 //$toks['PAGE_DESCRIPTION'] = $page_content->getDescription();
375 $toks['PAGE_KEYWORDS'] = GleanKeywords($page);
377 $template = new Template('html', $request);
379 // Handle other formats: So far we had html only.
380 // xml is requested by loaddump, rss is handled by RecentChanges,
381 // pdf is a special action, but should be a format to dump multiple pages
382 // if the actionpage plugin returns a pagelist.
383 // rdf, owl, kbmodel, daml, ... are handled by SemanticWeb.
384 /* Only single page versions. rss only if not already handled by RecentChanges.
386 if (!$format or $format == 'html' or $format == 'sidebar' or $format == 'contribs') {
387 $template->printExpansion($toks);
389 // No pagelist here. Single page version only
390 require_once 'lib/PageList.php';
391 $pagelist = new PageList();
392 $pagelist->addPage($page);
393 if ($format == 'pdf') {
394 require_once 'lib/pdf.php';
395 $request->setArg('format', '');
396 ConvertAndDisplayPdfPageList($request, $pagelist);
397 // time-sorted rdf a la RecentChanges
398 } elseif (in_array($format, array("rss91", "rss2", "rss", "atom"))) {
399 //$request->setArg('format','');
400 if ($pagename == _("RecentChanges"))
401 $template->printExpansion($toks);
403 require_once 'lib/plugin/RecentChanges.php';
404 $plugin = new WikiPlugin_RecentChanges();
405 $args = $request->getArgs();
406 return $plugin->format($plugin->getChanges($request->_dbi, $args), $args);
408 } elseif ($format == 'rdf') { // all semantic relations and attributes
409 require_once 'lib/SemanticWeb.php';
410 $rdf = new RdfWriter($request, $pagelist);
412 } elseif ($format == 'owl') { // or daml?
413 require_once 'lib/SemanticWeb.php';
414 $rdf = new OwlWriter($request, $pagelist);
416 } elseif ($format == 'json') { // include page content asynchronously
417 $request->setArg('format', '');
419 $content = $page_content->asXML();
422 $req_args = $request->args;
423 unset($req_args['format']);
424 // no meta-data so far, just the content
425 $json = array('content' => $content,
427 'phpwiki-version' => PHPWIKI_VERSION);
428 if (loadPhpExtension('json')) {
429 $json_enc = json_encode($json);
431 require_once 'lib/pear/JSON.php';
432 $j = new Services_JSON();
433 $json_enc = $j->encode($json);
435 header("Content-Type: application/json");
438 if (!in_array($pagename, array(_("LinkDatabase"))))
439 trigger_error(sprintf(_("Unsupported argument: %s=%s"), "format", $format),
441 $template->printExpansion($toks);
445 $page->increaseHitCount();
447 if ($request->getArg('action') != 'pdf') {
448 $request->checkValidators();
458 // c-hanging-comment-ender-p: nil
459 // indent-tabs-mode: nil