]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
fix RedirectTo button
[SourceForge/phpwiki.git] / lib / editpage.php
1 <?php
2 require_once 'lib/Template.php';
3 require_once 'lib/WikiUser.php';
4
5 class PageEditor
6 {
7     public $request;
8     public $user;
9     public $page;
10     /**
11      * @var WikiDB_PageRevision $current
12      */
13     public $current;
14     public $editaction;
15     public $locked;
16     public $public;
17     public $external;
18     public $_currentVersion;
19     /**
20      * @var UserPreferences $_prefs
21      */
22     private $_prefs;
23     private $_isSpam;
24     private $_wikicontent;
25
26     /**
27      * @param WikiRequest $request
28      */
29     function __construct(&$request)
30     {
31         $this->request = &$request;
32
33         $this->user = $request->getUser();
34         $this->page = $request->getPage();
35
36         $this->current = $this->page->getCurrentRevision(false);
37
38         // HACKish short circuit to browse on action=create
39         if ($request->getArg('action') == 'create') {
40             if (!$this->current->hasDefaultContents())
41                 $request->redirect(WikiURL($this->page->getName())); // noreturn
42         }
43
44         $this->meta = array('author' => $this->user->getId(),
45             'author_id' => $this->user->getAuthenticatedId(),
46             'mtime' => time());
47
48         $this->tokens = array();
49
50         if (defined('ENABLE_WYSIWYG') and ENABLE_WYSIWYG) {
51             $backend = WYSIWYG_BACKEND;
52             // TODO: error message
53             require_once("lib/WysiwygEdit/$backend.php");
54             $class = "WysiwygEdit_$backend";
55             $this->WysiwygEdit = new $class();
56         }
57         if (defined('ENABLE_CAPTCHA' and ENABLE_CAPTCHA)) {
58             require_once 'lib/Captcha.php';
59             $this->Captcha = new Captcha($this->meta);
60         }
61
62         $version = $request->getArg('version');
63         if ($version !== false) {
64             $this->selected = $this->page->getRevision($version);
65             $this->version = $version;
66         } else {
67             $this->version = $this->current->getVersion();
68             $this->selected = $this->page->getRevision($this->version);
69         }
70
71         if ($this->_restoreState()) {
72             $this->_initialEdit = false;
73         } else {
74             $this->_initializeState();
75             $this->_initialEdit = true;
76
77             // The edit request has specified some initial content from a template
78             if (($template = $request->getArg('template'))
79                 and $request->_dbi->isWikiPage($template)
80             ) {
81                 $page = $request->_dbi->getPage($template);
82                 $current = $page->getCurrentRevision();
83                 $this->_content = $current->getPackedContent();
84             } elseif ($initial_content = $request->getArg('initial_content')) {
85                 $this->_content = $initial_content;
86                 $this->_redirect_to = $request->getArg('save_and_redirect_to');
87             }
88         }
89         if (!headers_sent())
90             header("Content-Type: text/html; charset=UTF-8");
91     }
92
93     public function editPage()
94     {
95         $saveFailed = false;
96         $tokens = &$this->tokens;
97         $tokens['PAGE_LOCKED_MESSAGE'] = '';
98         $tokens['LOCK_CHANGED_MSG'] = '';
99         $tokens['CONCURRENT_UPDATE_MESSAGE'] = '';
100         $r =& $this->request;
101
102         if (isset($r->args['pref']['editWidth'])
103             and ($r->getPref('editWidth') != $r->args['pref']['editWidth'])
104         ) {
105             $r->_prefs->set('editWidth', $r->args['pref']['editWidth']);
106         }
107         if (isset($r->args['pref']['editHeight'])
108             and ($r->getPref('editHeight') != $r->args['pref']['editHeight'])
109         ) {
110             $r->_prefs->set('editHeight', $r->args['pref']['editHeight']);
111         }
112
113         if ($this->isModerated())
114             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getModeratedMessage();
115
116         if (!$this->canEdit()) {
117             if ($this->isInitialEdit())
118                 return $this->viewSource();
119             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
120         } elseif ($r->getArg('save_and_redirect_to') != "") {
121             if (defined('ENABLE_CAPTCHA') and ENABLE_CAPTCHA && $this->Captcha->Failed()) {
122                 $this->tokens['PAGE_LOCKED_MESSAGE'] =
123                     HTML::p(HTML::h1($this->Captcha->failed_msg));
124             } elseif ($this->savePage()) {
125                 // noreturn
126                 global $request;
127                 $request->setArg('action', false);
128                 $r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
129                 return true; // Page saved.
130             }
131             $saveFailed = true;
132         } elseif ($this->editaction == 'save') {
133             if (defined('ENABLE_CAPTCHA') and ENABLE_CAPTCHA && $this->Captcha->Failed()) {
134                 $this->tokens['PAGE_LOCKED_MESSAGE'] =
135                     HTML::p(HTML::h1($this->Captcha->failed_msg));
136             } elseif ($this->savePage()) {
137                 return true; // Page saved.
138             } else {
139                 $saveFailed = true;
140             }
141         } // coming from loadfile conflicts
142         elseif ($this->editaction == 'keep_old') {
143             // keep old page and do nothing
144             $this->_redirectToBrowsePage();
145             return true;
146         } elseif ($this->editaction == 'overwrite') {
147             // take the new content without diff
148             $source = $this->request->getArg('loadfile');
149             require_once 'lib/loadsave.php';
150             $this->request->setArg('loadfile', 1);
151             $this->request->setArg('overwrite', 1);
152             $this->request->setArg('merge', 0);
153             LoadFileOrDir($this->request);
154             $this->_redirectToBrowsePage();
155             return true;
156         } elseif ($this->editaction == 'upload') {
157             // run plugin UpLoad
158             $plugin = new WikiPluginLoader("UpLoad");
159             $plugin->run();
160             // add link to content
161             ;
162         }
163
164         if ($saveFailed and $this->isConcurrentUpdate()) {
165             // Get the text of the original page, and the two conflicting edits
166             // The diff3 class takes arrays as input.  So retrieve content as
167             // an array, or convert it as necesary.
168             $orig = $this->page->getRevision($this->_currentVersion);
169             // FIXME: what if _currentVersion has be deleted?
170             $orig_content = $orig->getContent();
171             $this_content = explode("\n", $this->_content);
172             $other_content = $this->current->getContent();
173             require_once 'lib/diff3.php';
174             $diff = new diff3($orig_content, $this_content, $other_content);
175             $output = $diff->merged_output(_("Your version"), _("Other version"));
176             // Set the content of the textarea to the merged diff
177             // output, and update the version
178             $this->_content = implode("\n", $output);
179             $this->_currentVersion = $this->current->getVersion();
180             $this->version = $this->_currentVersion;
181             $unresolved = $diff->ConflictingBlocks;
182             $tokens['CONCURRENT_UPDATE_MESSAGE']
183                 = $this->getConflictMessage($unresolved);
184         } elseif ($saveFailed && !$this->_isSpam) {
185             $tokens['CONCURRENT_UPDATE_MESSAGE'] =
186                 HTML(HTML::h2(_("Some internal editing error")),
187                     HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
188                     HTML::p(HTML::em(_("&version=-1 might help."))));
189         }
190
191         if ($this->editaction == 'edit_convert')
192             $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
193         if ($this->editaction == 'preview')
194             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
195         if ($this->editaction == 'diff')
196             $tokens['PREVIEW_CONTENT'] = $this->getDiff();
197
198         // FIXME: NOT_CURRENT_MESSAGE?
199         $tokens = array_merge($tokens, $this->getFormElements());
200
201         return $this->output('editpage', _("Edit: %s"));
202     }
203
204     public function output($template, $title_fs)
205     {
206         global $WikiTheme;
207         $selected = &$this->selected;
208         $current = &$this->current;
209
210         if ($selected && $selected->getVersion() != $current->getVersion()) {
211             $rev = $selected;
212             $pagelink = WikiLink($selected);
213         } else {
214             $rev = $current;
215             $pagelink = WikiLink($this->page);
216         }
217
218         $title = new FormattedText ($title_fs, $pagelink);
219         // not for dumphtml or viewsource
220         if (defined('ENABLE_WYSIWYG') and ENABLE_WYSIWYG and $template == 'editpage') {
221             $WikiTheme->addMoreHeaders($this->WysiwygEdit->Head());
222             //$tokens['PAGE_SOURCE'] = $this->WysiwygEdit->ConvertBefore($this->_content);
223         }
224         $template = Template($template, $this->tokens);
225         /* Tell google (and others) not to take notice of edit links */
226         if (defined('GOOGLE_LINKS_NOFOLLOW') and GOOGLE_LINKS_NOFOLLOW)
227             $args = array('ROBOTS_META' => "noindex,nofollow");
228         GeneratePage($template, $title, $rev);
229         return true;
230     }
231
232     public function viewSource()
233     {
234         assert($this->isInitialEdit());
235         assert($this->selected);
236
237         $this->tokens['PAGE_SOURCE'] = $this->_content;
238         $this->tokens['HIDDEN_INPUTS'] = HiddenInputs($this->request->getArgs());
239         return $this->output('viewsource', _("View Source: %s"));
240     }
241
242     private function updateLock()
243     {
244         $changed = false;
245         if (!ENABLE_PAGE_PUBLIC && !ENABLE_EXTERNAL_PAGES) {
246             if ((bool)$this->page->get('locked') == (bool)$this->locked)
247                 return false; // Not changed.
248         }
249
250         if (!$this->user->isAdmin()) {
251             // FIXME: some sort of message
252             return false; // not allowed.
253         }
254         if ((bool)$this->page->get('locked') != (bool)$this->locked) {
255             $this->page->set('locked', (bool)$this->locked);
256             $this->tokens['LOCK_CHANGED_MSG']
257                 .= ($this->locked
258                 ? _("Page now locked.")
259                 : _("Page now unlocked.") . " ");
260             $changed = true;
261         }
262         if (defined('ENABLE_PAGE_PUBLIC') and ENABLE_PAGE_PUBLIC and (bool)$this->page->get('public') != (bool)$this->public) {
263             $this->page->set('public', (bool)$this->public);
264             $this->tokens['LOCK_CHANGED_MSG']
265                 .= ($this->public
266                 ? _("Page now public.")
267                 : _("Page now not-public."));
268             $changed = true;
269         }
270
271         if (defined('ENABLE_EXTERNAL_PAGES') and ENABLE_EXTERNAL_PAGES) {
272             if ((bool)$this->page->get('external') != (bool)$this->external) {
273                 $this->page->set('external', (bool)$this->external);
274                 $this->tokens['LOCK_CHANGED_MSG']
275                     = ($this->external
276                     ? _("Page now external.")
277                     : _("Page now not-external.")) . " ";
278                 $changed = true;
279             }
280         }
281         return $changed; // lock changed.
282     }
283
284     public function savePage()
285     {
286         $request = &$this->request;
287
288         if ($this->isUnchanged()) {
289             // Allow admin lock/unlock even if
290             // no text changes were made.
291             if ($this->updateLock()) {
292                 $dbi = $request->getDbh();
293                 $dbi->touch();
294             }
295             // Save failed. No changes made.
296             $this->_redirectToBrowsePage();
297             return true;
298         }
299
300         if (!$this->user->isAdmin() and $this->isSpam()) {
301             $this->_isSpam = true;
302             return false;
303         }
304
305         $page = &$this->page;
306
307         // Include any meta-data from original page version which
308         // has not been explicitly updated.
309         $meta = $this->selected->getMetaData();
310         $meta = array_merge($meta, $this->meta);
311
312         // Save new revision
313         $this->_content = $this->getContent();
314         $newrevision = $page->save($this->_content,
315             $this->version == -1
316                 ? -1
317                 : $this->_currentVersion + 1,
318             // force new?
319             $meta);
320         if (!is_a($newrevision, 'WikiDB_PageRevision')) {
321             // Save failed.  (Concurrent updates).
322             return false;
323         }
324
325         // New contents successfully saved...
326         $this->updateLock();
327
328         // Clean out archived versions of this page.
329         require_once 'lib/ArchiveCleaner.php';
330         $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
331         $cleaner->cleanPageRevisions($page);
332
333         /* generate notification emails done in WikiDB::save to catch
334          all direct calls (admin plugins) */
335
336         // look at the errorstack
337         $errors = $GLOBALS['ErrorManager']->_postponed_errors;
338         $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
339         $GLOBALS['ErrorManager']->_postponed_errors = $errors;
340
341         $dbi = $request->getDbh();
342         $dbi->touch();
343
344         global $WikiTheme;
345         if (empty($warnings->_content) && !$WikiTheme->getImageURL('signature')) {
346             // Do redirect to browse page if no signature has
347             // been defined.  In this case, the user will most
348             // likely not see the rest of the HTML we generate
349             // (below).
350             $request->setArg('action', false);
351             $this->_redirectToBrowsePage();
352             return true;
353         }
354
355         // Force browse of current page version.
356         $request->setArg('version', false);
357         // testme: does preview and more need action=edit?
358         $request->setArg('action', false);
359
360         $template = Template('savepage', $this->tokens);
361         $template->replace('CONTENT', $newrevision->getTransformedContent());
362         if (!empty($warnings->_content)) {
363             $template->replace('WARNINGS', $warnings);
364             unset($GLOBALS['ErrorManager']->_postponed_errors);
365         }
366
367         $pagelink = WikiLink($page);
368
369         GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
370         return true;
371     }
372
373     protected function isConcurrentUpdate()
374     {
375         assert($this->current->getVersion() >= $this->_currentVersion);
376         return $this->current->getVersion() != $this->_currentVersion;
377     }
378
379     protected function canEdit()
380     {
381         return !$this->page->get('locked') || $this->user->isAdmin();
382     }
383
384     protected function isInitialEdit()
385     {
386         return $this->_initialEdit;
387     }
388
389     private function isUnchanged()
390     {
391         $current = &$this->current;
392         return $this->_content == $current->getPackedContent();
393     }
394
395     /**
396      * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
397      * Need to check dynamically some blacklist wikipage settings
398      * (plugin WikiAccessRestrictions) and some static blacklist.
399      * DONE:
400      *   More than NUM_SPAM_LINKS (default: 20) new external links.
401      *        Disabled if NUM_SPAM_LINKS is 0
402      *   ENABLE_SPAMASSASSIN:  content patterns by babycart (only php >= 4.3 for now)
403      *   ENABLE_SPAMBLOCKLIST: content domain blacklist
404      */
405     private function isSpam()
406     {
407         $current = &$this->current;
408         $request = &$this->request;
409
410         $oldtext = $current->getPackedContent();
411         $newtext =& $this->_content;
412         $numlinks = $this->numLinks($newtext);
413         $newlinks = $numlinks - $this->numLinks($oldtext);
414         // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
415         //        better use a certain text : link ratio.
416
417         // 1. Not more than NUM_SPAM_LINKS (default: 20) new external links
418         if ((NUM_SPAM_LINKS > 0) and ($newlinks >= NUM_SPAM_LINKS)) {
419             // Allow strictly authenticated users?
420             // TODO: mail the admin?
421             $this->tokens['PAGE_LOCKED_MESSAGE'] =
422                 HTML($this->getSpamMessage(),
423                     HTML::p(HTML::strong(_("Too many external links."))));
424             return true;
425         }
426         // 2. external babycart (SpamAssassin) check
427         // This will probably prevent from discussing sex or viagra related topics. So beware.
428         if (ENABLE_SPAMASSASSIN) {
429             require_once 'lib/spam_babycart.php';
430             if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
431                 $this->user->getId())
432             ) {
433                 // TODO: mail the admin
434                 if (is_array($babycart))
435                     $this->tokens['PAGE_LOCKED_MESSAGE'] =
436                         HTML($this->getSpamMessage(),
437                             HTML::p(HTML::em(_("SpamAssassin reports: "),
438                                 join("\n", $babycart))));
439                 return true;
440             }
441         }
442         // 3. extract (new) links and check surbl for blocked domains
443         if (defined('ENABLE_SPAMBLOCKLIST') and ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
444             require_once 'lib/SpamBlocklist.php';
445             require_once 'lib/InlineParser.php';
446             $parsed = TransformLinks($newtext);
447             $oldparsed = TransformLinks($oldtext);
448             $oldlinks = array();
449             foreach ($oldparsed->_content as $link) {
450                 if (is_a($link, 'Cached_ExternalLink') and !is_a($link, 'Cached_InterwikiLink')) {
451                     $uri = $link->_getURL($this->page->getName());
452                     $oldlinks[$uri]++;
453                 }
454             }
455             unset($oldparsed);
456             foreach ($parsed->_content as $link) {
457                 if (is_a($link, 'Cached_ExternalLink') and !is_a($link, 'Cached_InterwikiLink')) {
458                     $uri = $link->_getURL($this->page->getName());
459                     // only check new links, so admins may add blocked links.
460                     if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
461                         // TODO: mail the admin
462                         $this->tokens['PAGE_LOCKED_MESSAGE'] =
463                             HTML($this->getSpamMessage(),
464                                 HTML::p(HTML::strong(_("External links contain blocked domains:")),
465                                     HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
466                                         $uri . " [" . $res[2] . "]", $res[0], $res[1])))));
467                         return true;
468                     }
469                 }
470             }
471             unset($oldlinks);
472             unset($parsed);
473             unset($oldparsed);
474         }
475
476         return false;
477     }
478
479     /** Number of external links in the wikitext
480      */
481     private function numLinks(&$text)
482     {
483         return substr_count($text, "http://") + substr_count($text, "https://");
484     }
485
486     /** Header of the Anti Spam message
487      */
488     private function getSpamMessage()
489     {
490         return
491             HTML(HTML::h2(_("Spam Prevention")),
492                 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
493                     HTML::br(),
494                     _("Sorry for the inconvenience.")),
495                 HTML::p(""));
496     }
497
498     protected function getPreview()
499     {
500         require_once 'lib/PageType.php';
501         $this->_content = $this->getContent();
502         return new TransformedText($this->page, $this->_content, $this->meta);
503     }
504
505     protected function getConvertedPreview()
506     {
507         require_once 'lib/PageType.php';
508         $this->_content = $this->getContent();
509         return new TransformedText($this->page, $this->_content, $this->meta);
510     }
511
512     private function getDiff()
513     {
514         require_once 'lib/diff.php';
515         $html = HTML();
516
517         $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
518         if ($diff->isEmpty()) {
519             $html->pushContent(HTML::hr(),
520                                HTML::p(array('class' => 'warning_msg'),
521                                        _("Versions are identical")));
522         } else {
523             // New CSS formatted unified diffs
524             $fmt = new HtmlUnifiedDiffFormatter;
525             $html->pushContent($fmt->format($diff));
526         }
527         return $html;
528     }
529
530     // possibly convert HTMLAREA content back to Wiki markup
531     private function getContent()
532     {
533         if (ENABLE_WYSIWYG) {
534             // don't store everything as html
535             if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
536                 // Wikiwyg shortcut to avoid the InlineTransformer:
537                 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
538                 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
539                 $this->_content = join("", $xml_output->_content);
540             } else {
541                 $this->meta['pagetype'] = 'html';
542             }
543             return $this->_content;
544         } else {
545             return $this->_content;
546         }
547     }
548
549     protected function getLockedMessage()
550     {
551         return
552             HTML(HTML::h2(_("Page Locked")),
553                 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
554                 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
555                 HTML::p(_("Sorry for the inconvenience.")));
556     }
557
558     private function isModerated()
559     {
560         return $this->page->get('moderation');
561     }
562
563     private function getModeratedMessage()
564     {
565         return
566             HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
567                 HTML::p(fmt("You can edit away, but your changes will have to be approved by the defined moderators at the definition in %s", WikiLink(_("ModeratedPage")))),
568                 HTML::p(fmt("The approval has a grace period of 5 days. If you have your e-mail defined in your %s, you will get a notification of approval or rejection.",
569                     WikiLink(_("UserPreferences")))));
570     }
571
572     protected function getConflictMessage($unresolved = false)
573     {
574         /*
575          xgettext only knows about c/c++ line-continuation strings
576          it does not know about php's dot operator.
577          We want to translate this entire paragraph as one string, of course.
578          */
579
580         //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
581
582         if ($unresolved)
583             $message = HTML::p(fmt("Some of the changes could not automatically be combined.  Please look for sections beginning with ā€œ%sā€, and ending with ā€œ%sā€.  You will need to edit those sections by hand before you click Save.",
584                 "<<<<<<< " . _("Your version"),
585                 ">>>>>>> " . _("Other version")));
586         else
587             $message = HTML::p(_("Please check it through before saving."));
588
589         /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
590           HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
591                        $re_edit_link)),
592           HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
593           HTML::li(_("Save your updated changes.")));
594         */
595         return
596             HTML(HTML::h2(_("Conflicting Edits!")),
597                 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
598                 HTML::p(_("Your changes can not be saved as they are, since doing so would overwrite the other author's changes. So, your changes and those of the other author have been combined. The result is shown below.")),
599                 $message);
600     }
601
602     private function getTextArea()
603     {
604         global $WikiTheme;
605
606         $request = &$this->request;
607
608         $readonly = !$this->canEdit(); // || $this->isConcurrentUpdate();
609
610         // WYSIWYG will need two pagetypes: raw wikitest and converted html
611         if (ENABLE_WYSIWYG) {
612             $this->_wikicontent = $this->_content;
613             $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
614             //                $this->getPreview();
615             //$this->_htmlcontent = $this->_content->asXML();
616         }
617
618         $textarea = HTML::textarea(array('class' => 'wikiedit',
619                 'name' => 'edit[content]',
620                 'id' => 'edit-content',
621                 'rows' => $request->getPref('editHeight'),
622                 'cols' => $request->getPref('editWidth'),
623                 'readonly' => (bool)$readonly),
624             $this->_content);
625
626         if (defined('JS_SEARCHREPLACE') and JS_SEARCHREPLACE) {
627             $this->tokens['JS_SEARCHREPLACE'] = 1;
628             $undo_btn = $WikiTheme->getImageURL("ed_undo.png");
629             $undo_d_btn = $WikiTheme->getImageURL("ed_undo_d.png");
630             // JS_SEARCHREPLACE from walterzorn.de
631             $js = Javascript("
632 uri_undo_btn   = '" . $undo_btn . "'
633 msg_undo_alt   = '" . _("Undo") . "'
634 uri_undo_d_btn = '" . $undo_d_btn . "'
635 msg_undo_d_alt = '" . _("Undo disabled") . "'
636 msg_do_undo    = '" . _("Operation undone") . "'
637 msg_replfound  = '" . _("Substring ā€œ\\1ā€ found \\2 times. Replace with ā€œ\\3ā€?") . "'
638 msg_replnot    = '" . _("String ā€œ%sā€ not found.") . "'
639 msg_repl_title     = '" . _("Search & Replace") . "'
640 msg_repl_search    = '" . _("Search for") . "'
641 msg_repl_replace_with = '" . _("Replace with") . "'
642 msg_repl_ok        = '" . _("OK") . "'
643 msg_repl_close     = '" . _("Close") . "'
644 ");
645             if (empty($WikiTheme->_headers_printed)) {
646                 $WikiTheme->addMoreHeaders($js);
647                 $WikiTheme->addMoreAttr('body', "SearchReplace", " onload='define_f()'");
648             } else { // from an actionpage: WikiBlog, AddComment, WikiForum
649                 printXML($js);
650             }
651         } else {
652             $WikiTheme->addMoreAttr('body', "editfocus", "document.getElementById('edit-content]').editarea.focus()");
653         }
654
655         if (defined('ENABLE_WYSIWYG') and ENABLE_WYSIWYG) {
656             return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
657                 $textarea->getAttr('name'));
658         } elseif (defined('ENABLE_EDIT_TOOLBAR') and ENABLE_EDIT_TOOLBAR) {
659             $init = JavaScript("var data_path = '" . javascript_quote_string(DATA_PATH) . "';\n");
660             $js = JavaScript('', array('src' => $WikiTheme->_findData("toolbar.js")));
661             if (empty($WikiTheme->_headers_printed)) {
662                 $WikiTheme->addMoreHeaders($init);
663                 $WikiTheme->addMoreHeaders($js);
664             } else { // from an actionpage: WikiBlog, AddComment, WikiForum
665                 printXML($init);
666                 printXML($js);
667                 printXML(JavaScript('define_f()'));
668             }
669             $toolbar = HTML::div(array('class' => 'edit-toolbar', 'id' => 'toolbar'));
670             $toolbar->pushContent(HTML::input(array('src' => $WikiTheme->getImageURL("ed_save.png"),
671                                                     'name' => 'edit[save]',
672                                                     'class' => 'toolbar',
673                                                     'alt' => _('Save'),
674                                                     'title' => _('Save'),
675                                                     'type' => 'image')));
676             $toolbar->pushContent(HTML::input(array('src' => $WikiTheme->getImageURL("ed_preview.png"),
677                                                     'name' => 'edit[preview]',
678                                                     'class' => 'toolbar',
679                                                     'alt' => _('Preview'),
680                                                     'title' => _('Preview'),
681                                                     'type' => 'image')));
682             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_format_bold.png"),
683                                                   'class' => 'toolbar',
684                                                   'alt' => _('Bold text'),
685                                                   'title' => _('Bold text'),
686                                                   'onclick' => "insertTags('**','**','"._('Bold text')."'); return true;")));
687             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_format_italic.png"),
688                                                   'class' => 'toolbar',
689                                                   'alt' => _('Italic text'),
690                                                   'title' => _('Italic text'),
691                                                   'onclick' => "insertTags('//','//','"._('Italic text')."'); return true;")));
692             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_format_strike.png"),
693                                                   'class' => 'toolbar',
694                                                   'alt' => _('Strike-through text'),
695                                                   'title' => _('Strike-through text'),
696                                                   'onclick' => "insertTags('<s>','</s>','"._('Strike-through text')."'); return true;")));
697             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_format_color.png"),
698                                                   'class' => 'toolbar',
699                                                   'alt' => _('Color text'),
700                                                   'title' => _('Color text'),
701                                                   'onclick' => "insertTags('%color=green%','%%','"._('Color text')."'); return true;")));
702             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_pagelink.png"),
703                                                   'class' => 'toolbar',
704                                                   'alt' => _('Link to page'),
705                                                   'title' => _('Link to page'),
706                                                   'onclick' => "insertTags('[[',']]','"._('PageName|optional label')."'); return true;")));
707             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_link.png"),
708                                                   'class' => 'toolbar',
709                                                   'alt' => _('External link (remember http:// prefix)'),
710                                                   'title' => _('External link (remember http:// prefix)'),
711                                                   'onclick' => "insertTags('[[',']]','"._('http://www.example.com|optional label')."'); return true;")));
712             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_headline.png"),
713                                                   'class' => 'toolbar',
714                                                   'alt' => _('Level 1 headline'),
715                                                   'title' => _('Level 1 headline'),
716                                                   'onclick' => 'insertTags("\n== "," ==\n","'._("Headline text").'"); return true;')));
717             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_nowiki.png"),
718                                                   'class' => 'toolbar',
719                                                   'alt' => _('Ignore wiki formatting'),
720                                                   'title' => _('Ignore wiki formatting'),
721                                                   'onclick' => 'insertTags("<verbatim>\n","\n</verbatim>","'._("Insert non-formatted text here").'"); return true;')));
722             global $request;
723             $username = $request->_user->UserName();
724             $signature = " ā€“ā€“[[" . $username . "]] " . CTime() . '\n';
725             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_sig.png"),
726                                                   'class' => 'toolbar',
727                                                   'alt' => _('Your signature'),
728                                                   'title' => _('Your signature'),
729                                                   'onclick' => "insertTags('".$signature."','',''); return true;")));
730             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_hr.png"),
731                                                   'class' => 'toolbar',
732                                                   'alt' => _('Horizontal line'),
733                                                   'title' => _('Horizontal line'),
734                                                   'onclick' => 'insertTags("\n----\n","",""); return true;')));
735             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_table.png"),
736                                                   'class' => 'toolbar',
737                                                   'alt' => _('Sample table'),
738                                                   'title' => _('Sample table'),
739                                                   'onclick' => 'insertTags("\n{| class=\"bordered\"\n|+ This is the table caption\n|-\n! Header A !! Header B !! Header C\n|-\n| Cell A1 || Cell B1 || Cell C1\n|-\n| Cell A2 || Cell B2 || Cell C2\n|-\n| Cell A3 || Cell B3 || Cell C3\n|}\n","",""); return true;')));
740             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_enumlist.png"),
741                                                   'class' => 'toolbar',
742                                                   'alt' => _('Enumeration'),
743                                                   'title' => _('Enumeration'),
744                                                   'onclick' => 'insertTags("\n# Item 1\n# Item 2\n# Item 3\n","",""); return true;')));
745             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_list.png"),
746                                                   'class' => 'toolbar',
747                                                   'alt' => _('List'),
748                                                   'title' => _('List'),
749                                                   'onclick' => 'insertTags("\n* Item 1\n* Item 2\n* Item 3\n","",""); return true;')));
750             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_toc.png"),
751                                                   'class' => 'toolbar',
752                                                   'alt' => _('Table of Contents'),
753                                                   'title' => _('Table of Contents'),
754                                                   'onclick' => 'insertTags("<<CreateToc with_toclink||=1>>\n","",""); return true;')));
755             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_redirect.png"),
756                                                   'class' => 'toolbar',
757                                                   'alt' => _('Redirect'),
758                                                   'title' => _('Redirect'),
759                                                   'onclick' => "insertTags('<<RedirectTo page=\"','\">>','"._('Page Name')."'); return true;")));
760             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_templateplugin.png"),
761                                                   'class' => 'toolbar',
762                                                   'alt' => _('Insert Dynamic Template'),
763                                                   'title' => _('Insert Dynamic Template'),
764                                                   'onclick' => "insertTags('{{','}}','"._('Template Name')."'); return true;")));
765             if (defined('TOOLBAR_TEMPLATE_PULLDOWN') and TOOLBAR_TEMPLATE_PULLDOWN) {
766                 $toolbar->pushContent($this->templatePulldown(TOOLBAR_TEMPLATE_PULLDOWN));
767             }
768             $toolbar->pushContent($this->categoriesPulldown());
769             $toolbar->pushContent($this->pluginPulldown());
770             if (defined('TOOLBAR_PAGELINK_PULLDOWN') and TOOLBAR_PAGELINK_PULLDOWN) {
771                 $toolbar->pushContent($this->pagesPulldown(TOOLBAR_PAGELINK_PULLDOWN));
772             }
773             if (defined('TOOLBAR_IMAGE_PULLDOWN') and TOOLBAR_IMAGE_PULLDOWN) {
774                 $toolbar->pushContent($this->imagePulldown());
775             }
776             if (defined('JS_SEARCHREPLACE') and JS_SEARCHREPLACE) {
777                 $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_replace.png"),
778                                                       'class' => 'toolbar',
779                                                       'alt' => _('Search & Replace'),
780                                                       'title' => _('Search & Replace'),
781                                                       'onclick' => "replace();")));
782                 $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_undo_d.png"),
783                                                       'class' => 'toolbar',
784                                                       'id' => 'sr_undo',
785                                                       'alt' => _('Undo Search & Replace'),
786                                                       'title' => _('Undo Search & Replace'),
787                                                       'onclick' => "do_undo();")));
788             }
789             return HTML($toolbar, $textarea);
790         } else {
791             return $textarea;
792         }
793     }
794
795     private function categoriesPulldown()
796     {
797         /**
798          * @var WikiRequest $request
799          */
800         global $request;
801         global $WikiTheme;
802
803         require_once 'lib/TextSearchQuery.php';
804         $dbi =& $request->_dbi;
805         // KEYWORDS formerly known as $KeywordLinkRegexp
806         $pages = $dbi->titleSearch(new TextSearchQuery(KEYWORDS, true));
807         if ($pages) {
808             $categories = array();
809             while ($p = $pages->next()) {
810                 $page = $p->getName();
811                 $categories[] = "['$page', '%0A----%0A%5B%5B" . $page . "%5D%5D']";
812             }
813             if (!$categories) return '';
814             // Ensure this to be inserted at the very end. Hence we added the id to the function.
815             $more_buttons = HTML::img(array('class' => "toolbar",
816                 'id' => 'tb-categories',
817                 'src' => $WikiTheme->getImageURL("ed_category.png"),
818                 'title' => _("Insert Categories"),
819                 'alt' => _("Insert Categories"), // to detect this at js
820                 'onclick' => "showPulldown('" .
821                     _("Insert Categories")
822                     . "',[" . join(",", $categories) . "],'"
823                     . _("Insert") . "','"
824                     . _("Close") . "','tb-categories')"));
825             return $more_buttons;
826         }
827         return '';
828     }
829
830     private function pluginPulldown()
831     {
832         global $WikiTheme;
833         global $AllAllowedPlugins;
834
835         $plugin_dir = 'lib/plugin';
836         if (defined('PHPWIKI_DIR'))
837             $plugin_dir = PHPWIKI_DIR . "/$plugin_dir";
838         $pd = new fileSet($plugin_dir, '*.php');
839         $plugins = $pd->getFiles();
840         unset($pd);
841         sort($plugins);
842         if (!empty($plugins)) {
843             $plugin_js = '';
844             require_once 'lib/WikiPlugin.php';
845             $w = new WikiPluginLoader();
846             foreach ($plugins as $plugin) {
847                 $pluginName = str_replace(".php", "", $plugin);
848                 if (in_array($pluginName, $AllAllowedPlugins)) {
849                     $p = $w->getPlugin($pluginName, false); // second arg?
850                     // trap php files which aren't WikiPlugin~s
851                     if (strtolower(substr(get_parent_class($p), 0, 10)) == 'wikiplugin') {
852                         $plugin_args = '';
853                         $desc = $p->getArgumentsDescription();
854                         $src = array("\n", '"', "'", '|', '[', ']', '\\');
855                         $replace = array('%0A', '%22', '%27', '%7C', '%5B', '%5D', '%5C');
856                         $desc = str_replace("<br />", ' ', $desc->asXML());
857                         if ($desc)
858                             $plugin_args = ' ' . str_replace($src, $replace, $desc);
859                         $toinsert = "%0A<<" . $pluginName . $plugin_args . ">>"; // args?
860                         $plugin_js .= ",['$pluginName','$toinsert']";
861                     }
862                 }
863             }
864             $plugin_js = substr($plugin_js, 1);
865             $more_buttons = HTML::img(array('class' => "toolbar",
866                 'id' => 'tb-plugins',
867                 'src' => $WikiTheme->getImageURL("ed_plugins.png"),
868                 'title' => _("Insert Plugin"),
869                 'alt' => _("Insert Plugin"),
870                 'onclick' => "showPulldown('" .
871                     _("Insert Plugin")
872                     . "',[" . $plugin_js . "],'"
873                     . _("Insert") . "','"
874                     . _("Close") . "','tb-plugins')"));
875             return $more_buttons;
876         }
877         return '';
878     }
879
880     private function pagesPulldown($query)
881     {
882         /**
883          * @var WikiRequest $request
884          */
885         global $request;
886
887         require_once 'lib/TextSearchQuery.php';
888         $dbi =& $request->_dbi;
889         $page_iter = $dbi->titleSearch(new TextSearchQuery($query, false, 'auto'));
890         if ($page_iter->count() > 0) {
891             global $WikiTheme;
892             $pages = array();
893             while ($p = $page_iter->next()) {
894                 $page = $p->getName();
895                 $pages[] = "['$page', '%5B%5B" . $page . "%5D%5D']";
896             }
897             return HTML::img(array('class' => "toolbar",
898                 'id' => 'tb-pages',
899                 'src' => $WikiTheme->getImageURL("ed_pages.png"),
900                 'title' => _("Insert PageLink"),
901                 'alt' => _("Insert PageLink"),
902                 'onclick' => "showPulldown('" .
903                     _("Insert PageLink")
904                     . "',[" . join(",", $pages) . "],'"
905                     . _("Insert") . "','"
906                     . _("Close") . "','tb-pages')"));
907         }
908         return '';
909     }
910
911     private function templatePulldown($query)
912     {
913         global $request;
914         require_once 'lib/TextSearchQuery.php';
915         $dbi =& $request->_dbi;
916         $page_iter = $dbi->titleSearch(new TextSearchQuery($query, false, 'auto'));
917         if ($page_iter->count()) {
918             global $WikiTheme;
919             $pages_js = '';
920             while ($p = $page_iter->next()) {
921                 $rev = $p->getCurrentRevision();
922                 $toinsert = str_replace(array("\n", '"'), array('__nl__', '__quot__'), $rev->_get_content());
923                 $pages_js .= ",['" . $p->getName() . "','__nl__$toinsert']";
924             }
925             $pages_js = substr($pages_js, 1);
926             if (!empty($pages_js))
927                 return HTML::img
928                 (array('class' => "toolbar",
929                     'id' => 'tb-templates',
930                     'src' => $WikiTheme->getImageURL("ed_template.png"),
931                     'title' => _("Insert Static Template"),
932                     'alt' => _("Insert Static Template"),
933                     'onclick' => "showPulldown('" .
934                         _("Insert Static Template")
935                         . "',[" . $pages_js . "],'"
936                         . _("Insert") . "','"
937                         . _("Close") . "','tb-templates')"));
938         }
939         return '';
940     }
941
942     private function imagePulldown()
943     {
944         global $WikiTheme, $request;
945
946         $image_dir = getUploadFilePath();
947         $pd = new imageOrVideoSet($image_dir, '*');
948         $images = $pd->getFiles();
949         unset($pd);
950         if (defined('UPLOAD_USERDIR') and UPLOAD_USERDIR) {
951             $image_dir .= "/" . $request->_user->_userid;
952             $pd = new imageOrVideoSet($image_dir, '*');
953             $images = array_merge($images, $pd->getFiles());
954             unset($pd);
955         }
956         sort($images);
957         if (!empty($images)) {
958             $image_js = '';
959             foreach ($images as $image) {
960                 $image_js .= ",['$image','{{" . $image . "}}']";
961             }
962             $image_js = substr($image_js, 1);
963             $more_buttons = HTML::img(array('class' => "toolbar",
964                 'id' => 'tb-images',
965                 'src' => $WikiTheme->getImageURL("ed_image.png"),
966                 'title' => _("Insert Image or Video"),
967                 'alt' => _("Insert Image or Video"),
968                 'onclick' => "showPulldown('" .
969                     _("Insert Image or Video")
970                     . "',[" . $image_js . "],'"
971                     . _("Insert") . "','"
972                     . _("Close") . "','tb-images')"));
973             return $more_buttons;
974         }
975         return '';
976     }
977
978     protected function getFormElements()
979     {
980         global $WikiTheme;
981         $request = &$this->request;
982         $page = &$this->page;
983
984         $h = array('action' => 'edit',
985             'pagename' => $page->getName(),
986             'version' => $this->version,
987             'edit[pagetype]' => $this->meta['pagetype'],
988             'edit[current_version]' => $this->_currentVersion);
989
990         $el['HIDDEN_INPUTS'] = HiddenInputs($h);
991         $el['EDIT_TEXTAREA'] = $this->getTextArea();
992         if (ENABLE_CAPTCHA) {
993             $el = array_merge($el, $this->Captcha->getFormElements());
994         }
995         $el['SUMMARY_INPUT']
996             = HTML::input(array('type' => 'text',
997             'class' => 'wikitext',
998             'id' => 'edit-summary',
999             'name' => 'edit[summary]',
1000             'size' => 50,
1001             'maxlength' => 256,
1002             'value' => $this->meta['summary']));
1003         $el['MINOR_EDIT_CB']
1004             = HTML::input(array('type' => 'checkbox',
1005             'name' => 'edit[minor_edit]',
1006             'id' => 'edit-minor_edit',
1007             'checked' => (bool)$this->meta['is_minor_edit']));
1008         $el['LOCKED_CB']
1009             = HTML::input(array('type' => 'checkbox',
1010             'name' => 'edit[locked]',
1011             'id' => 'edit-locked',
1012             'disabled' => (bool)!$this->user->isAdmin(),
1013             'checked' => (bool)$this->locked));
1014         if (ENABLE_PAGE_PUBLIC) {
1015             $el['PUBLIC_CB']
1016                 = HTML::input(array('type' => 'checkbox',
1017                 'name' => 'edit[public]',
1018                 'id' => 'edit-public',
1019                 'disabled' => (bool)!$this->user->isAdmin(),
1020                 'checked' => (bool)$this->page->get('public')));
1021         }
1022         if (ENABLE_EXTERNAL_PAGES) {
1023             $el['EXTERNAL_CB']
1024                 = HTML::input(array('type' => 'checkbox',
1025                 'name' => 'edit[external]',
1026                 'id' => 'edit-external',
1027                 'disabled' => (bool)!$this->user->isAdmin(),
1028                 'checked' => (bool)$this->page->get('external')));
1029         }
1030         if (ENABLE_WYSIWYG) {
1031             if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
1032                 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
1033             }
1034         }
1035         $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
1036             'wikiaction',
1037             array('accesskey' => 'p',
1038                 'title' => _('Preview the current content [alt-p]')));
1039         $el['SAVE_B'] = Button('submit:edit[save]',
1040             _("Save"), 'wikiaction',
1041             array('accesskey' => 's',
1042                 'title' => _('Save the current content as wikipage [alt-s]')));
1043         $el['CHANGES_B'] = Button('submit:edit[diff]',
1044             _("Changes"), 'wikiaction',
1045             array('accesskey' => 'c',
1046                 'title' => _('Preview the current changes as diff [alt-c]')));
1047         $el['UPLOAD_B'] = Button('submit:edit[upload]',
1048             _("Upload"), 'wikiaction',
1049             array('title' => _('Select a local file and press Upload to attach into this page')));
1050         $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
1051             _("Spell Check"), 'wikiaction',
1052             array('title' => _('Check the spelling')));
1053         $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
1054         $el['SEP'] = $WikiTheme->getButtonSeparator();
1055         $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
1056             HTML::em($this->user->getId()));
1057
1058         return $el;
1059     }
1060
1061     private function _redirectToBrowsePage()
1062     {
1063         $this->request->redirect(WikiURL($this->page, array(), 'absolute_url'));
1064     }
1065
1066     private function _restoreState()
1067     {
1068         $request = &$this->request;
1069
1070         $posted = $request->getArg('edit');
1071         $request->setArg('edit', false);
1072
1073         if (!$posted
1074             || !$request->isPost()
1075             || !in_array($request->getArg('action'), array('edit', 'loadfile'))
1076         )
1077             return false;
1078
1079         if (!isset($posted['content']) || !is_string($posted['content']))
1080             return false;
1081         $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
1082             rtrim($posted['content']));
1083         $this->_content = $this->getContent();
1084
1085         $this->_currentVersion = (int)$posted['current_version'];
1086
1087         if ($this->_currentVersion < 0)
1088             return false;
1089         if ($this->_currentVersion > $this->current->getVersion())
1090             return false; // FIXME: some kind of warning?
1091
1092         $meta['summary'] = trim(substr($posted['summary'], 0, 256));
1093         $meta['is_minor_edit'] = !empty($posted['minor_edit']);
1094         $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
1095         if (ENABLE_CAPTCHA)
1096             $meta['captcha_input'] = !empty($posted['captcha_input']) ?
1097                 $posted['captcha_input'] : '';
1098
1099         $this->meta = array_merge($this->meta, $meta);
1100         $this->locked = !empty($posted['locked']);
1101         if (ENABLE_PAGE_PUBLIC)
1102             $this->public = !empty($posted['public']);
1103         if (ENABLE_EXTERNAL_PAGES)
1104             $this->external = !empty($posted['external']);
1105
1106         foreach (array('preview', 'save', 'edit_convert',
1107                      'keep_old', 'overwrite', 'diff', 'upload') as $o) {
1108             if (!empty($posted[$o]))
1109                 $this->editaction = $o;
1110         }
1111         if (empty($this->editaction))
1112             $this->editaction = 'edit';
1113
1114         return true;
1115     }
1116
1117     private function _initializeState()
1118     {
1119         $request = &$this->request;
1120         $current = &$this->current;
1121         $selected = &$this->selected;
1122         $user = &$this->user;
1123
1124         if (!$selected)
1125             NoSuchRevision($request, $this->page, $this->version); // noreturn
1126
1127         $this->_currentVersion = $current->getVersion();
1128         $this->_content = $selected->getPackedContent();
1129
1130         $this->locked = $this->page->get('locked');
1131
1132         // If author same as previous author, default minor_edit to on.
1133         $age = $this->meta['mtime'] - $current->get('mtime');
1134         $this->meta['is_minor_edit'] = ($age < MINOR_EDIT_TIMEOUT
1135             && $current->get('author') == $user->getId()
1136         );
1137
1138         $this->meta['pagetype'] = $selected->get('pagetype');
1139         if ($this->meta['pagetype'] == 'wikiblog')
1140             $this->meta['summary'] = $selected->get('summary'); // keep blog title
1141         else
1142             $this->meta['summary'] = '';
1143         $this->editaction = 'edit';
1144     }
1145 }
1146
1147 class LoadFileConflictPageEditor
1148     extends PageEditor
1149 {
1150     public function editPage($saveFailed = true)
1151     {
1152         $tokens = &$this->tokens;
1153
1154         if (!$this->canEdit()) {
1155             if ($this->isInitialEdit()) {
1156                 return $this->viewSource();
1157             }
1158             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
1159         } elseif ($this->editaction == 'save') {
1160             if ($this->savePage()) {
1161                 return true; // Page saved.
1162             }
1163             $saveFailed = true;
1164         }
1165
1166         if ($saveFailed || $this->isConcurrentUpdate()) {
1167             // Get the text of the original page, and the two conflicting edits
1168             // The diff class takes arrays as input.  So retrieve content as
1169             // an array, or convert it as necesary.
1170             $orig = $this->page->getRevision($this->_currentVersion);
1171             $this_content = explode("\n", $this->_content);
1172             $other_content = $this->current->getContent();
1173             require_once 'lib/diff.php';
1174             $diff2 = new Diff($other_content, $this_content);
1175             $context_lines = max(4, count($other_content) + 1,
1176                 count($this_content) + 1);
1177             $fmt = new BlockDiffFormatter($context_lines);
1178
1179             $this->_content = $fmt->format($diff2);
1180             // FIXME: integrate this into class BlockDiffFormatter
1181             $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
1182                 $this->_content);
1183             $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
1184                 $this->_content);
1185
1186             $this->_currentVersion = $this->current->getVersion();
1187             $this->version = $this->_currentVersion;
1188             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
1189         }
1190
1191         if ($this->editaction == 'edit_convert')
1192             $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
1193         if ($this->editaction == 'preview')
1194             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
1195
1196         // FIXME: NOT_CURRENT_MESSAGE?
1197         $tokens = array_merge($tokens, $this->getFormElements());
1198         // we need all GET params for loadfile overwrite
1199         if ($this->request->getArg('action') == 'loadfile') {
1200
1201             $this->tokens['HIDDEN_INPUTS'] =
1202                 HTML(HiddenInputs
1203                     (array('source' => $this->request->getArg('source'),
1204                         'merge' => 1)),
1205                     $this->tokens['HIDDEN_INPUTS']);
1206             // add two conflict resolution buttons before preview and save.
1207             $tokens['PREVIEW_B'] = HTML(
1208                 Button('submit:edit[keep_old]',
1209                     _("Keep old"), 'wikiaction'),
1210                 $tokens['SEP'],
1211                 Button('submit:edit[overwrite]',
1212                     _("Overwrite with new"), 'wikiaction'),
1213                 $tokens['SEP'],
1214                 $tokens['PREVIEW_B']);
1215         }
1216         return $this->output('editpage', _("Merge and Edit: %s"));
1217     }
1218
1219     public function output($template, $title_fs)
1220     {
1221         $selected = &$this->selected;
1222         $current = &$this->current;
1223
1224         if ($selected && $selected->getVersion() != $current->getVersion()) {
1225             $pagelink = WikiLink($selected);
1226         } else {
1227             $pagelink = WikiLink($this->page);
1228         }
1229
1230         $title = new FormattedText ($title_fs, $pagelink);
1231         $this->tokens['HEADER'] = $title;
1232         //hack! there's no TITLE in editpage, but in the previous top template
1233         if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
1234             $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
1235         else
1236             $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
1237                 $this->tokens['PAGE_LOCKED_MESSAGE']);
1238         $template = Template($template, $this->tokens);
1239
1240         //GeneratePage($template, $title, $rev);
1241         PrintXML($template);
1242         return true;
1243     }
1244
1245     protected function getConflictMessage($unresolved = false)
1246     {
1247         $message = HTML(HTML::p(fmt("Some of the changes could not automatically be combined.  Please look for sections beginning with ā€œ%sā€, and ending with ā€œ%sā€.  You will need to edit those sections by hand before you click Save.",
1248                 "<<<<<<<",
1249                 "======="),
1250             HTML::p(_("Please check it through before saving."))));
1251         return $message;
1252     }
1253 }
1254
1255 // Local Variables:
1256 // mode: php
1257 // tab-width: 8
1258 // c-basic-offset: 4
1259 // c-hanging-comment-ender-p: nil
1260 // indent-tabs-mode: nil
1261 // End: