]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
Translate strings
[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=&quot;','&quot;>>','Page Name'); return true;")));
760             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_templateplugin.png"),
761                                                   'class' => 'toolbar',
762                                                   'alt' => _('Template'),
763                                                   'title' => _('Template'),
764                                                   'onclick' => "insertTags('{{','}}','Template Name'); return true;")));
765             $toolbar->pushContent($this->categoriesPulldown());
766             $toolbar->pushContent($this->pluginPulldown());
767             if (defined('TOOLBAR_PAGELINK_PULLDOWN') and TOOLBAR_PAGELINK_PULLDOWN) {
768                 $toolbar->pushContent($this->pagesPulldown(TOOLBAR_PAGELINK_PULLDOWN));
769             }
770             if (defined('TOOLBAR_TEMPLATE_PULLDOWN') and TOOLBAR_TEMPLATE_PULLDOWN) {
771                 $toolbar->pushContent($this->templatePulldown(TOOLBAR_TEMPLATE_PULLDOWN));
772             }
773             if (defined('TOOLBAR_IMAGE_PULLDOWN') and TOOLBAR_IMAGE_PULLDOWN) {
774                 $toolbar->pushContent($this->imagePulldown());
775             }
776             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_replace.png"),
777                                                   'class' => 'toolbar',
778                                                   'alt' => _('Search & Replace'),
779                                                   'title' => _('Search & Replace'),
780                                                   'onclick' => "replace();")));
781             $toolbar->pushContent(HTML::img(array('src' => $WikiTheme->getImageURL("ed_undo_d.png"),
782                                                   'class' => 'toolbar',
783                                                   'id' => 'sr_undo',
784                                                   'alt' => _('Undo Search & Replace'),
785                                                   'title' => _('Undo Search & Replace'),
786                                                   'onclick' => "do_undo();")));
787             return HTML($toolbar, $textarea);
788         } else {
789             return $textarea;
790         }
791     }
792
793     private function categoriesPulldown()
794     {
795         /**
796          * @var WikiRequest $request
797          */
798         global $request;
799         global $WikiTheme;
800
801         require_once 'lib/TextSearchQuery.php';
802         $dbi =& $request->_dbi;
803         // KEYWORDS formerly known as $KeywordLinkRegexp
804         $pages = $dbi->titleSearch(new TextSearchQuery(KEYWORDS, true));
805         if ($pages) {
806             $categories = array();
807             while ($p = $pages->next()) {
808                 $page = $p->getName();
809                 $categories[] = "['$page', '%0A----%0A%5B%5B" . $page . "%5D%5D']";
810             }
811             if (!$categories) return '';
812             // Ensure this to be inserted at the very end. Hence we added the id to the function.
813             $more_buttons = HTML::img(array('class' => "toolbar",
814                 'id' => 'tb-categories',
815                 'src' => $WikiTheme->getImageURL("ed_category.png"),
816                 'title' => _("Insert Categories"),
817                 'alt' => _("Insert Categories"), // to detect this at js
818                 'onclick' => "showPulldown('" .
819                     _("Insert Categories")
820                     . "',[" . join(",", $categories) . "],'"
821                     . _("Insert") . "','"
822                     . _("Close") . "','tb-categories')"));
823             return $more_buttons;
824         }
825         return '';
826     }
827
828     private function pluginPulldown()
829     {
830         global $WikiTheme;
831         global $AllAllowedPlugins;
832
833         $plugin_dir = 'lib/plugin';
834         if (defined('PHPWIKI_DIR'))
835             $plugin_dir = PHPWIKI_DIR . "/$plugin_dir";
836         $pd = new fileSet($plugin_dir, '*.php');
837         $plugins = $pd->getFiles();
838         unset($pd);
839         sort($plugins);
840         if (!empty($plugins)) {
841             $plugin_js = '';
842             require_once 'lib/WikiPlugin.php';
843             $w = new WikiPluginLoader();
844             foreach ($plugins as $plugin) {
845                 $pluginName = str_replace(".php", "", $plugin);
846                 if (in_array($pluginName, $AllAllowedPlugins)) {
847                     $p = $w->getPlugin($pluginName, false); // second arg?
848                     // trap php files which aren't WikiPlugin~s
849                     if (strtolower(substr(get_parent_class($p), 0, 10)) == 'wikiplugin') {
850                         $plugin_args = '';
851                         $desc = $p->getArgumentsDescription();
852                         $src = array("\n", '"', "'", '|', '[', ']', '\\');
853                         $replace = array('%0A', '%22', '%27', '%7C', '%5B', '%5D', '%5C');
854                         $desc = str_replace("<br />", ' ', $desc->asXML());
855                         if ($desc)
856                             $plugin_args = ' ' . str_replace($src, $replace, $desc);
857                         $toinsert = "%0A<<" . $pluginName . $plugin_args . ">>"; // args?
858                         $plugin_js .= ",['$pluginName','$toinsert']";
859                     }
860                 }
861             }
862             $plugin_js = substr($plugin_js, 1);
863             $more_buttons = HTML::img(array('class' => "toolbar",
864                 'id' => 'tb-plugins',
865                 'src' => $WikiTheme->getImageURL("ed_plugins.png"),
866                 'title' => _("Insert Plugin"),
867                 'alt' => _("Insert Plugin"),
868                 'onclick' => "showPulldown('" .
869                     _("Insert Plugin")
870                     . "',[" . $plugin_js . "],'"
871                     . _("Insert") . "','"
872                     . _("Close") . "','tb-plugins')"));
873             return $more_buttons;
874         }
875         return '';
876     }
877
878     private function pagesPulldown($query)
879     {
880         /**
881          * @var WikiRequest $request
882          */
883         global $request;
884
885         require_once 'lib/TextSearchQuery.php';
886         $dbi =& $request->_dbi;
887         $page_iter = $dbi->titleSearch(new TextSearchQuery($query, false, 'auto'));
888         if ($page_iter->count() > 0) {
889             global $WikiTheme;
890             $pages = array();
891             while ($p = $page_iter->next()) {
892                 $page = $p->getName();
893                 $pages[] = "['$page', '%5B%5B" . $page . "%5D%5D']";
894             }
895             return HTML::img(array('class' => "toolbar",
896                 'id' => 'tb-pages',
897                 'src' => $WikiTheme->getImageURL("ed_pages.png"),
898                 'title' => _("Insert PageLink"),
899                 'alt' => _("Insert PageLink"),
900                 'onclick' => "showPulldown('" .
901                     _("Insert PageLink")
902                     . "',[" . join(",", $pages) . "],'"
903                     . _("Insert") . "','"
904                     . _("Close") . "','tb-pages')"));
905         }
906         return '';
907     }
908
909     private function templatePulldown($query)
910     {
911         global $request;
912         require_once 'lib/TextSearchQuery.php';
913         $dbi =& $request->_dbi;
914         $page_iter = $dbi->titleSearch(new TextSearchQuery($query, false, 'auto'));
915         if ($page_iter->count()) {
916             global $WikiTheme;
917             $pages_js = '';
918             while ($p = $page_iter->next()) {
919                 $rev = $p->getCurrentRevision();
920                 $toinsert = str_replace(array("\n", '"'), array('_nl', '_quot'), $rev->_get_content());
921                 $pages_js .= ",['" . $p->getName() . "','_nl$toinsert']";
922             }
923             $pages_js = substr($pages_js, 1);
924             if (!empty($pages_js))
925                 return HTML::img
926                 (array('class' => "toolbar",
927                     'id' => 'tb-templates',
928                     'src' => $WikiTheme->getImageURL("ed_template.png"),
929                     'title' => _("Insert Template"),
930                     'alt' => _("Insert Template"),
931                     'onclick' => "showPulldown('" .
932                         _("Insert Template")
933                         . "',[" . $pages_js . "],'"
934                         . _("Insert") . "','"
935                         . _("Close") . "','tb-templates')"));
936         }
937         return '';
938     }
939
940     private function imagePulldown()
941     {
942         global $WikiTheme, $request;
943
944         $image_dir = getUploadFilePath();
945         $pd = new imageOrVideoSet($image_dir, '*');
946         $images = $pd->getFiles();
947         unset($pd);
948         if (defined('UPLOAD_USERDIR') and UPLOAD_USERDIR) {
949             $image_dir .= "/" . $request->_user->_userid;
950             $pd = new imageOrVideoSet($image_dir, '*');
951             $images = array_merge($images, $pd->getFiles());
952             unset($pd);
953         }
954         sort($images);
955         if (!empty($images)) {
956             $image_js = '';
957             foreach ($images as $image) {
958                 $image_js .= ",['$image','{{" . $image . "}}']";
959             }
960             $image_js = substr($image_js, 1);
961             $more_buttons = HTML::img(array('class' => "toolbar",
962                 'id' => 'tb-images',
963                 'src' => $WikiTheme->getImageURL("ed_image.png"),
964                 'title' => _("Insert Image or Video"),
965                 'alt' => _("Insert Image or Video"),
966                 'onclick' => "showPulldown('" .
967                     _("Insert Image or Video")
968                     . "',[" . $image_js . "],'"
969                     . _("Insert") . "','"
970                     . _("Close") . "','tb-images')"));
971             return $more_buttons;
972         }
973         return '';
974     }
975
976     protected function getFormElements()
977     {
978         global $WikiTheme;
979         $request = &$this->request;
980         $page = &$this->page;
981
982         $h = array('action' => 'edit',
983             'pagename' => $page->getName(),
984             'version' => $this->version,
985             'edit[pagetype]' => $this->meta['pagetype'],
986             'edit[current_version]' => $this->_currentVersion);
987
988         $el['HIDDEN_INPUTS'] = HiddenInputs($h);
989         $el['EDIT_TEXTAREA'] = $this->getTextArea();
990         if (ENABLE_CAPTCHA) {
991             $el = array_merge($el, $this->Captcha->getFormElements());
992         }
993         $el['SUMMARY_INPUT']
994             = HTML::input(array('type' => 'text',
995             'class' => 'wikitext',
996             'id' => 'edit-summary',
997             'name' => 'edit[summary]',
998             'size' => 50,
999             'maxlength' => 256,
1000             'value' => $this->meta['summary']));
1001         $el['MINOR_EDIT_CB']
1002             = HTML::input(array('type' => 'checkbox',
1003             'name' => 'edit[minor_edit]',
1004             'id' => 'edit-minor_edit',
1005             'checked' => (bool)$this->meta['is_minor_edit']));
1006         $el['LOCKED_CB']
1007             = HTML::input(array('type' => 'checkbox',
1008             'name' => 'edit[locked]',
1009             'id' => 'edit-locked',
1010             'disabled' => (bool)!$this->user->isAdmin(),
1011             'checked' => (bool)$this->locked));
1012         if (ENABLE_PAGE_PUBLIC) {
1013             $el['PUBLIC_CB']
1014                 = HTML::input(array('type' => 'checkbox',
1015                 'name' => 'edit[public]',
1016                 'id' => 'edit-public',
1017                 'disabled' => (bool)!$this->user->isAdmin(),
1018                 'checked' => (bool)$this->page->get('public')));
1019         }
1020         if (ENABLE_EXTERNAL_PAGES) {
1021             $el['EXTERNAL_CB']
1022                 = HTML::input(array('type' => 'checkbox',
1023                 'name' => 'edit[external]',
1024                 'id' => 'edit-external',
1025                 'disabled' => (bool)!$this->user->isAdmin(),
1026                 'checked' => (bool)$this->page->get('external')));
1027         }
1028         if (ENABLE_WYSIWYG) {
1029             if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
1030                 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
1031             }
1032         }
1033
1034         $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
1035             'wikiaction',
1036             array('accesskey' => 'p',
1037                 'title' => _('Preview the current content [alt-p]')));
1038
1039         //if (!$this->isConcurrentUpdate() && $this->canEdit())
1040         $el['SAVE_B'] = Button('submit:edit[save]',
1041             _("Save"), 'wikiaction',
1042             array('accesskey' => 's',
1043                 'title' => _('Save the current content as wikipage [alt-s]')));
1044         $el['CHANGES_B'] = Button('submit:edit[diff]',
1045             _("Changes"), 'wikiaction',
1046             array('accesskey' => 'c',
1047                 'title' => _('Preview the current changes as diff [alt-c]')));
1048         $el['UPLOAD_B'] = Button('submit:edit[upload]',
1049             _("Upload"), 'wikiaction',
1050             array('title' => _('Select a local file and press Upload to attach into this page')));
1051         $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
1052             _("Spell Check"), 'wikiaction',
1053             array('title' => _('Check the spelling')));
1054         $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
1055
1056         $el['WIDTH_PREF']
1057             = HTML::input(array('type' => 'text',
1058             'size' => 3,
1059             'maxlength' => 4,
1060             'class' => "numeric",
1061             'name' => 'pref[editWidth]',
1062             'id' => 'pref-editWidth',
1063             'value' => $request->getPref('editWidth'),
1064             'onchange' => 'this.form.submit();'));
1065         $el['HEIGHT_PREF']
1066             = HTML::input(array('type' => 'text',
1067             'size' => 3,
1068             'maxlength' => 4,
1069             'class' => "numeric",
1070             'name' => 'pref[editHeight]',
1071             'id' => 'pref-editHeight',
1072             'value' => $request->getPref('editHeight'),
1073             'onchange' => 'this.form.submit();'));
1074         $el['SEP'] = $WikiTheme->getButtonSeparator();
1075         $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
1076             HTML::em($this->user->getId()));
1077
1078         return $el;
1079     }
1080
1081     private function _redirectToBrowsePage()
1082     {
1083         $this->request->redirect(WikiURL($this->page, array(), 'absolute_url'));
1084     }
1085
1086     private function _restoreState()
1087     {
1088         $request = &$this->request;
1089
1090         $posted = $request->getArg('edit');
1091         $request->setArg('edit', false);
1092
1093         if (!$posted
1094             || !$request->isPost()
1095             || !in_array($request->getArg('action'), array('edit', 'loadfile'))
1096         )
1097             return false;
1098
1099         if (!isset($posted['content']) || !is_string($posted['content']))
1100             return false;
1101         $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
1102             rtrim($posted['content']));
1103         $this->_content = $this->getContent();
1104
1105         $this->_currentVersion = (int)$posted['current_version'];
1106
1107         if ($this->_currentVersion < 0)
1108             return false;
1109         if ($this->_currentVersion > $this->current->getVersion())
1110             return false; // FIXME: some kind of warning?
1111
1112         $meta['summary'] = trim(substr($posted['summary'], 0, 256));
1113         $meta['is_minor_edit'] = !empty($posted['minor_edit']);
1114         $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
1115         if (ENABLE_CAPTCHA)
1116             $meta['captcha_input'] = !empty($posted['captcha_input']) ?
1117                 $posted['captcha_input'] : '';
1118
1119         $this->meta = array_merge($this->meta, $meta);
1120         $this->locked = !empty($posted['locked']);
1121         if (ENABLE_PAGE_PUBLIC)
1122             $this->public = !empty($posted['public']);
1123         if (ENABLE_EXTERNAL_PAGES)
1124             $this->external = !empty($posted['external']);
1125
1126         foreach (array('preview', 'save', 'edit_convert',
1127                      'keep_old', 'overwrite', 'diff', 'upload') as $o) {
1128             if (!empty($posted[$o]))
1129                 $this->editaction = $o;
1130         }
1131         if (empty($this->editaction))
1132             $this->editaction = 'edit';
1133
1134         return true;
1135     }
1136
1137     private function _initializeState()
1138     {
1139         $request = &$this->request;
1140         $current = &$this->current;
1141         $selected = &$this->selected;
1142         $user = &$this->user;
1143
1144         if (!$selected)
1145             NoSuchRevision($request, $this->page, $this->version); // noreturn
1146
1147         $this->_currentVersion = $current->getVersion();
1148         $this->_content = $selected->getPackedContent();
1149
1150         $this->locked = $this->page->get('locked');
1151
1152         // If author same as previous author, default minor_edit to on.
1153         $age = $this->meta['mtime'] - $current->get('mtime');
1154         $this->meta['is_minor_edit'] = ($age < MINOR_EDIT_TIMEOUT
1155             && $current->get('author') == $user->getId()
1156         );
1157
1158         $this->meta['pagetype'] = $selected->get('pagetype');
1159         if ($this->meta['pagetype'] == 'wikiblog')
1160             $this->meta['summary'] = $selected->get('summary'); // keep blog title
1161         else
1162             $this->meta['summary'] = '';
1163         $this->editaction = 'edit';
1164     }
1165 }
1166
1167 class LoadFileConflictPageEditor
1168     extends PageEditor
1169 {
1170     public function editPage($saveFailed = true)
1171     {
1172         $tokens = &$this->tokens;
1173
1174         if (!$this->canEdit()) {
1175             if ($this->isInitialEdit()) {
1176                 return $this->viewSource();
1177             }
1178             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
1179         } elseif ($this->editaction == 'save') {
1180             if ($this->savePage()) {
1181                 return true; // Page saved.
1182             }
1183             $saveFailed = true;
1184         }
1185
1186         if ($saveFailed || $this->isConcurrentUpdate()) {
1187             // Get the text of the original page, and the two conflicting edits
1188             // The diff class takes arrays as input.  So retrieve content as
1189             // an array, or convert it as necesary.
1190             $orig = $this->page->getRevision($this->_currentVersion);
1191             $this_content = explode("\n", $this->_content);
1192             $other_content = $this->current->getContent();
1193             require_once 'lib/diff.php';
1194             $diff2 = new Diff($other_content, $this_content);
1195             $context_lines = max(4, count($other_content) + 1,
1196                 count($this_content) + 1);
1197             $fmt = new BlockDiffFormatter($context_lines);
1198
1199             $this->_content = $fmt->format($diff2);
1200             // FIXME: integrate this into class BlockDiffFormatter
1201             $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
1202                 $this->_content);
1203             $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
1204                 $this->_content);
1205
1206             $this->_currentVersion = $this->current->getVersion();
1207             $this->version = $this->_currentVersion;
1208             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
1209         }
1210
1211         if ($this->editaction == 'edit_convert')
1212             $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
1213         if ($this->editaction == 'preview')
1214             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
1215
1216         // FIXME: NOT_CURRENT_MESSAGE?
1217         $tokens = array_merge($tokens, $this->getFormElements());
1218         // we need all GET params for loadfile overwrite
1219         if ($this->request->getArg('action') == 'loadfile') {
1220
1221             $this->tokens['HIDDEN_INPUTS'] =
1222                 HTML(HiddenInputs
1223                     (array('source' => $this->request->getArg('source'),
1224                         'merge' => 1)),
1225                     $this->tokens['HIDDEN_INPUTS']);
1226             // add two conflict resolution buttons before preview and save.
1227             $tokens['PREVIEW_B'] = HTML(
1228                 Button('submit:edit[keep_old]',
1229                     _("Keep old"), 'wikiaction'),
1230                 $tokens['SEP'],
1231                 Button('submit:edit[overwrite]',
1232                     _("Overwrite with new"), 'wikiaction'),
1233                 $tokens['SEP'],
1234                 $tokens['PREVIEW_B']);
1235         }
1236         return $this->output('editpage', _("Merge and Edit: %s"));
1237     }
1238
1239     public function output($template, $title_fs)
1240     {
1241         $selected = &$this->selected;
1242         $current = &$this->current;
1243
1244         if ($selected && $selected->getVersion() != $current->getVersion()) {
1245             $pagelink = WikiLink($selected);
1246         } else {
1247             $pagelink = WikiLink($this->page);
1248         }
1249
1250         $title = new FormattedText ($title_fs, $pagelink);
1251         $this->tokens['HEADER'] = $title;
1252         //hack! there's no TITLE in editpage, but in the previous top template
1253         if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
1254             $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
1255         else
1256             $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
1257                 $this->tokens['PAGE_LOCKED_MESSAGE']);
1258         $template = Template($template, $this->tokens);
1259
1260         //GeneratePage($template, $title, $rev);
1261         PrintXML($template);
1262         return true;
1263     }
1264
1265     protected function getConflictMessage($unresolved = false)
1266     {
1267         $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.",
1268                 "<<<<<<<",
1269                 "======="),
1270             HTML::p(_("Please check it through before saving."))));
1271         return $message;
1272     }
1273 }
1274
1275 // Local Variables:
1276 // mode: php
1277 // tab-width: 8
1278 // c-basic-offset: 4
1279 // c-hanging-comment-ender-p: nil
1280 // indent-tabs-mode: nil
1281 // End: