]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
Remove unused variable
[SourceForge/phpwiki.git] / lib / editpage.php
1 <?php
2 require_once 'lib/Template.php';
3
4 class PageEditor
5 {
6     function PageEditor(&$request)
7     {
8         $this->request = &$request;
9
10         $this->user = $request->getUser();
11         $this->page = $request->getPage();
12
13         $this->current = $this->page->getCurrentRevision(false);
14
15         // HACKish short circuit to browse on action=create
16         if ($request->getArg('action') == 'create') {
17             if (!$this->current->hasDefaultContents())
18                 $request->redirect(WikiURL($this->page->getName())); // noreturn
19         }
20
21         $this->meta = array('author' => $this->user->getId(),
22             'author_id' => $this->user->getAuthenticatedId(),
23             'mtime' => time());
24
25         $this->tokens = array();
26
27         if (ENABLE_WYSIWYG) {
28             $backend = WYSIWYG_BACKEND;
29             // TODO: error message
30             require_once("lib/WysiwygEdit/$backend.php");
31             $class = "WysiwygEdit_$backend";
32             $this->WysiwygEdit = new $class();
33         }
34         if (ENABLE_CAPTCHA) {
35             require_once 'lib/Captcha.php';
36             $this->Captcha = new Captcha($this->meta);
37         }
38
39         $version = $request->getArg('version');
40         if ($version !== false) {
41             $this->selected = $this->page->getRevision($version);
42             $this->version = $version;
43         } else {
44             $this->version = $this->current->getVersion();
45             $this->selected = $this->page->getRevision($this->version);
46         }
47
48         if ($this->_restoreState()) {
49             $this->_initialEdit = false;
50         } else {
51             $this->_initializeState();
52             $this->_initialEdit = true;
53
54             // The edit request has specified some initial content from a template
55             if (($template = $request->getArg('template'))
56                 and $request->_dbi->isWikiPage($template)
57             ) {
58                 $page = $request->_dbi->getPage($template);
59                 $current = $page->getCurrentRevision();
60                 $this->_content = $current->getPackedContent();
61             } elseif ($initial_content = $request->getArg('initial_content')) {
62                 $this->_content = $initial_content;
63                 $this->_redirect_to = $request->getArg('save_and_redirect_to');
64             }
65         }
66         if (!headers_sent())
67             header("Content-Type: text/html; charset=" . $GLOBALS['charset']);
68     }
69
70     function editPage()
71     {
72         global $WikiTheme;
73         $saveFailed = false;
74         $tokens = &$this->tokens;
75         $tokens['PAGE_LOCKED_MESSAGE'] = '';
76         $tokens['LOCK_CHANGED_MSG'] = '';
77         $tokens['CONCURRENT_UPDATE_MESSAGE'] = '';
78         $r =& $this->request;
79
80         if (isset($r->args['pref']['editWidth'])
81             and ($r->getPref('editWidth') != $r->args['pref']['editWidth'])
82         ) {
83             $r->_prefs->set('editWidth', $r->args['pref']['editWidth']);
84         }
85         if (isset($r->args['pref']['editHeight'])
86             and ($r->getPref('editHeight') != $r->args['pref']['editHeight'])
87         ) {
88             $r->_prefs->set('editHeight', $r->args['pref']['editHeight']);
89         }
90
91         if ($this->isModerated())
92             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getModeratedMessage();
93
94         if (!$this->canEdit()) {
95             if ($this->isInitialEdit())
96                 return $this->viewSource();
97             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
98         } elseif ($r->getArg('save_and_redirect_to') != "") {
99             if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
100                 $this->tokens['PAGE_LOCKED_MESSAGE'] =
101                     HTML::p(HTML::h1($this->Captcha->failed_msg));
102             } elseif ($this->savePage()) {
103                 // noreturn
104                 $request->setArg('action', false);
105                 $r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
106                 return true; // Page saved.
107             }
108             $saveFailed = true;
109         } elseif ($this->editaction == 'save') {
110             if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
111                 $this->tokens['PAGE_LOCKED_MESSAGE'] =
112                     HTML::p(HTML::h1($this->Captcha->failed_msg));
113             } elseif ($this->savePage()) {
114                 return true; // Page saved.
115             } else {
116                 $saveFailed = true;
117             }
118         } // coming from loadfile conflicts
119         elseif ($this->editaction == 'keep_old') {
120             // keep old page and do nothing
121             $this->_redirectToBrowsePage();
122             //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
123             return true;
124         } elseif ($this->editaction == 'overwrite') {
125             // take the new content without diff
126             $source = $this->request->getArg('loadfile');
127             require_once 'lib/loadsave.php';
128             $this->request->setArg('loadfile', 1);
129             $this->request->setArg('overwrite', 1);
130             $this->request->setArg('merge', 0);
131             LoadFileOrDir($this->request);
132             $this->_redirectToBrowsePage();
133             //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
134             return true;
135         } elseif ($this->editaction == 'upload') {
136             // run plugin UpLoad
137             $plugin = WikiPluginLoader("UpLoad");
138             $plugin->run();
139             // add link to content
140             ;
141         }
142
143         if ($saveFailed and $this->isConcurrentUpdate()) {
144             // Get the text of the original page, and the two conflicting edits
145             // The diff3 class takes arrays as input.  So retrieve content as
146             // an array, or convert it as necesary.
147             $orig = $this->page->getRevision($this->_currentVersion);
148             // FIXME: what if _currentVersion has be deleted?
149             $orig_content = $orig->getContent();
150             $this_content = explode("\n", $this->_content);
151             $other_content = $this->current->getContent();
152             require_once 'lib/diff3.php';
153             $diff = new diff3($orig_content, $this_content, $other_content);
154             $output = $diff->merged_output(_("Your version"), _("Other version"));
155             // Set the content of the textarea to the merged diff
156             // output, and update the version
157             $this->_content = implode("\n", $output);
158             $this->_currentVersion = $this->current->getVersion();
159             $this->version = $this->_currentVersion;
160             $unresolved = $diff->ConflictingBlocks;
161             $tokens['CONCURRENT_UPDATE_MESSAGE']
162                 = $this->getConflictMessage($unresolved);
163         } elseif ($saveFailed && !$this->_isSpam) {
164             $tokens['CONCURRENT_UPDATE_MESSAGE'] =
165                 HTML(HTML::h2(_("Some internal editing error")),
166                     HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
167                     HTML::p(HTML::em(_("&version=-1 might help."))));
168         }
169
170         if ($this->editaction == 'edit_convert')
171             $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
172         if ($this->editaction == 'preview')
173             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
174         if ($this->editaction == 'diff')
175             $tokens['PREVIEW_CONTENT'] = $this->getDiff();
176
177         // FIXME: NOT_CURRENT_MESSAGE?
178         $tokens = array_merge($tokens, $this->getFormElements());
179
180         if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
181             require_once 'lib/EditToolbar.php';
182             $toolbar = new EditToolbar();
183             $tokens = array_merge($tokens, $toolbar->getTokens());
184         }
185
186         return $this->output('editpage', _("Edit: %s"));
187     }
188
189     function output($template, $title_fs)
190     {
191         global $WikiTheme;
192         $selected = &$this->selected;
193         $current = &$this->current;
194
195         if ($selected && $selected->getVersion() != $current->getVersion()) {
196             $rev = $selected;
197             $pagelink = WikiLink($selected);
198         } else {
199             $rev = $current;
200             $pagelink = WikiLink($this->page);
201         }
202
203         $title = new FormattedText ($title_fs, $pagelink);
204         // not for dumphtml or viewsource
205         if (ENABLE_WYSIWYG and $template == 'editpage') {
206             $WikiTheme->addMoreHeaders($this->WysiwygEdit->Head());
207             //$tokens['PAGE_SOURCE'] = $this->WysiwygEdit->ConvertBefore($this->_content);
208         }
209         $template = Template($template, $this->tokens);
210         /* Tell google (and others) not to take notice of edit links */
211         if (GOOGLE_LINKS_NOFOLLOW)
212             $args = array('ROBOTS_META' => "noindex,nofollow");
213         GeneratePage($template, $title, $rev);
214         return true;
215     }
216
217     function viewSource()
218     {
219         assert($this->isInitialEdit());
220         assert($this->selected);
221
222         $this->tokens['PAGE_SOURCE'] = $this->_content;
223         $this->tokens['HIDDEN_INPUTS'] = HiddenInputs($this->request->getArgs());
224         return $this->output('viewsource', _("View Source: %s"));
225     }
226
227     function updateLock()
228     {
229         $changed = false;
230         if (!ENABLE_PAGE_PUBLIC && !ENABLE_EXTERNAL_PAGES) {
231             if ((bool)$this->page->get('locked') == (bool)$this->locked)
232                 return false; // Not changed.
233         }
234
235         if (!$this->user->isAdmin()) {
236             // FIXME: some sort of message
237             return false; // not allowed.
238         }
239         if ((bool)$this->page->get('locked') != (bool)$this->locked) {
240             $this->page->set('locked', (bool)$this->locked);
241             $this->tokens['LOCK_CHANGED_MSG']
242                 .= ($this->locked
243                 ? _("Page now locked.")
244                 : _("Page now unlocked.") . " ");
245             $changed = true;
246         }
247         if (ENABLE_PAGE_PUBLIC and (bool)$this->page->get('public') != (bool)$this->public) {
248             $this->page->set('public', (bool)$this->public);
249             $this->tokens['LOCK_CHANGED_MSG']
250                 .= ($this->public
251                 ? _("Page now public.")
252                 : _("Page now not-public."));
253             $changed = true;
254         }
255
256         if (ENABLE_EXTERNAL_PAGES) {
257             if ((bool)$this->page->get('external') != (bool)$this->external) {
258                 $this->page->set('external', (bool)$this->external);
259                 $this->tokens['LOCK_CHANGED_MSG']
260                     = ($this->external
261                     ? _("Page now external.")
262                     : _("Page now not-external.")) . " ";
263                 $changed = true;
264             }
265         }
266         return $changed; // lock changed.
267     }
268
269     function savePage()
270     {
271         $request = &$this->request;
272
273         if ($this->isUnchanged()) {
274             // Allow admin lock/unlock even if
275             // no text changes were made.
276             if ($this->updateLock()) {
277                 $dbi = $request->getDbh();
278                 $dbi->touch();
279             }
280             // Save failed. No changes made.
281             $this->_redirectToBrowsePage();
282             // user will probably not see the rest of this...
283             require_once 'lib/display.php';
284             // force browse of current version:
285             $request->setArg('action', false);
286             $request->setArg('version', false);
287             displayPage($request, 'nochanges');
288             return true;
289         }
290
291         if (!$this->user->isAdmin() and $this->isSpam()) {
292             $this->_isSpam = true;
293             return false;
294             /*
295             // Save failed. No changes made.
296             $this->_redirectToBrowsePage();
297             // user will probably not see the rest of this...
298             require_once 'lib/display.php';
299             // force browse of current version:
300             $request->setArg('version', false);
301             displayPage($request, 'nochanges');
302             return true;
303             */
304         }
305
306         $page = &$this->page;
307
308         // Include any meta-data from original page version which
309         // has not been explicitly updated.
310         // (Except don't propagate pgsrc_version --- moot for now,
311         //  because at present it never gets into the db...)
312         $meta = $this->selected->getMetaData();
313         unset($meta['pgsrc_version']);
314         $meta = array_merge($meta, $this->meta);
315
316         // Save new revision
317         $this->_content = $this->getContent();
318         $newrevision = $page->save($this->_content,
319             $this->version == -1
320                 ? -1
321                 : $this->_currentVersion + 1,
322             // force new?
323             $meta);
324         if (!isa($newrevision, 'WikiDB_PageRevision')) {
325             // Save failed.  (Concurrent updates).
326             return false;
327         }
328
329         // New contents successfully saved...
330         $this->updateLock();
331
332         // Clean out archived versions of this page.
333         require_once 'lib/ArchiveCleaner.php';
334         $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
335         $cleaner->cleanPageRevisions($page);
336
337         /* generate notification emails done in WikiDB::save to catch
338          all direct calls (admin plugins) */
339
340         // look at the errorstack
341         $errors = $GLOBALS['ErrorManager']->_postponed_errors;
342         $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
343         $GLOBALS['ErrorManager']->_postponed_errors = $errors;
344
345         $dbi = $request->getDbh();
346         $dbi->touch();
347
348         global $WikiTheme;
349         if (empty($warnings->_content) && !$WikiTheme->getImageURL('signature')) {
350             // Do redirect to browse page if no signature has
351             // been defined.  In this case, the user will most
352             // likely not see the rest of the HTML we generate
353             // (below).
354             $request->setArg('action', false);
355             $this->_redirectToBrowsePage();
356         }
357
358         // Force browse of current page version.
359         $request->setArg('version', false);
360         // testme: does preview and more need action=edit?
361         $request->setArg('action', false);
362
363         $template = Template('savepage', $this->tokens);
364         $template->replace('CONTENT', $newrevision->getTransformedContent());
365         if (!empty($warnings->_content)) {
366             $template->replace('WARNINGS', $warnings);
367             unset($GLOBALS['ErrorManager']->_postponed_errors);
368         }
369
370         $pagelink = WikiLink($page);
371
372         GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
373         return true;
374     }
375
376     function isConcurrentUpdate()
377     {
378         assert($this->current->getVersion() >= $this->_currentVersion);
379         return $this->current->getVersion() != $this->_currentVersion;
380     }
381
382     function canEdit()
383     {
384         return !$this->page->get('locked') || $this->user->isAdmin();
385     }
386
387     function isInitialEdit()
388     {
389         return $this->_initialEdit;
390     }
391
392     function isUnchanged()
393     {
394         $current = &$this->current;
395
396         if ($this->meta['markup'] != $current->get('markup'))
397             return false;
398
399         return $this->_content == $current->getPackedContent();
400     }
401
402     /**
403      * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
404      * Need to check dynamically some blacklist wikipage settings
405      * (plugin WikiAccessRestrictions) and some static blacklist.
406      * DONE:
407      *   More than NUM_SPAM_LINKS (default: 20) new external links.
408      *        Disabled if NUM_SPAM_LINKS is 0
409      *   ENABLE_SPAMASSASSIN:  content patterns by babycart (only php >= 4.3 for now)
410      *   ENABLE_SPAMBLOCKLIST: content domain blacklist
411      */
412     function isSpam()
413     {
414         $current = &$this->current;
415         $request = &$this->request;
416
417         $oldtext = $current->getPackedContent();
418         $newtext =& $this->_content;
419         $numlinks = $this->numLinks($newtext);
420         $newlinks = $numlinks - $this->numLinks($oldtext);
421         // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
422         //        better use a certain text : link ratio.
423
424         // 1. Not more than NUM_SPAM_LINKS (default: 20) new external links
425         if ((NUM_SPAM_LINKS > 0) and ($newlinks >= NUM_SPAM_LINKS)) {
426             // Allow strictly authenticated users?
427             // TODO: mail the admin?
428             $this->tokens['PAGE_LOCKED_MESSAGE'] =
429                 HTML($this->getSpamMessage(),
430                     HTML::p(HTML::strong(_("Too many external links."))));
431             return true;
432         }
433         // 2. external babycart (SpamAssassin) check
434         // This will probably prevent from discussing sex or viagra related topics. So beware.
435         if (ENABLE_SPAMASSASSIN) {
436             require_once 'lib/spam_babycart.php';
437             if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
438                 $this->user->getId())
439             ) {
440                 // TODO: mail the admin
441                 if (is_array($babycart))
442                     $this->tokens['PAGE_LOCKED_MESSAGE'] =
443                         HTML($this->getSpamMessage(),
444                             HTML::p(HTML::em(_("SpamAssassin reports: "),
445                                 join("\n", $babycart))));
446                 return true;
447             }
448         }
449         // 3. extract (new) links and check surbl for blocked domains
450         if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
451             require_once 'lib/SpamBlocklist.php';
452             require_once 'lib/InlineParser.php';
453             $parsed = TransformLinks($newtext);
454             $oldparsed = TransformLinks($oldtext);
455             $oldlinks = array();
456             foreach ($oldparsed->_content as $link) {
457                 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
458                     $uri = $link->_getURL($this->page->getName());
459                     $oldlinks[$uri]++;
460                 }
461             }
462             unset($oldparsed);
463             foreach ($parsed->_content as $link) {
464                 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
465                     $uri = $link->_getURL($this->page->getName());
466                     // only check new links, so admins may add blocked links.
467                     if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
468                         // TODO: mail the admin
469                         $this->tokens['PAGE_LOCKED_MESSAGE'] =
470                             HTML($this->getSpamMessage(),
471                                 HTML::p(HTML::strong(_("External links contain blocked domains:")),
472                                     HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
473                                         $uri . " [" . $res[2] . "]", $res[0], $res[1])))));
474                         return true;
475                     }
476                 }
477             }
478             unset($oldlinks);
479             unset($parsed);
480             unset($oldparsed);
481         }
482
483         return false;
484     }
485
486     /** Number of external links in the wikitext
487      */
488     function numLinks(&$text)
489     {
490         return substr_count($text, "http://") + substr_count($text, "https://");
491     }
492
493     /** Header of the Anti Spam message
494      */
495     function getSpamMessage()
496     {
497         return
498             HTML(HTML::h2(_("Spam Prevention")),
499                 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
500                     HTML::br(),
501                     _("Sorry for the inconvenience.")),
502                 HTML::p(""));
503     }
504
505     function getPreview()
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     function getConvertedPreview()
513     {
514         require_once 'lib/PageType.php';
515         $this->_content = $this->getContent();
516         $this->meta['markup'] = 2.0;
517         $this->_content = ConvertOldMarkup($this->_content);
518         return new TransformedText($this->page, $this->_content, $this->meta);
519     }
520
521     function getDiff()
522     {
523         require_once 'lib/diff.php';
524         $html = HTML();
525
526         $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
527         if ($diff->isEmpty()) {
528             $html->pushContent(HTML::hr(),
529                 HTML::p('[', _("Versions are identical"),
530                     ']'));
531         } else {
532             // New CSS formatted unified diffs
533             $fmt = new HtmlUnifiedDiffFormatter;
534             $html->pushContent($fmt->format($diff));
535         }
536         return $html;
537     }
538
539     // possibly convert HTMLAREA content back to Wiki markup
540     function getContent()
541     {
542         if (ENABLE_WYSIWYG) {
543             // don't store everything as html
544             if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
545                 // Wikiwyg shortcut to avoid the InlineTransformer:
546                 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
547                 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
548                 $this->_content = join("", $xml_output->_content);
549             } else {
550                 $this->meta['pagetype'] = 'html';
551             }
552             return $this->_content;
553         } else {
554             return $this->_content;
555         }
556     }
557
558     function getLockedMessage()
559     {
560         return
561             HTML(HTML::h2(_("Page Locked")),
562                 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
563                 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
564                 HTML::p(_("Sorry for the inconvenience.")));
565     }
566
567     function isModerated()
568     {
569         return $this->page->get('moderation');
570     }
571
572     function getModeratedMessage()
573     {
574         return
575             HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
576                 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")))),
577                 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.",
578                     WikiLink(_("UserPreferences")))));
579     }
580
581     function getConflictMessage($unresolved = false)
582     {
583         /*
584          xgettext only knows about c/c++ line-continuation strings
585          it does not know about php's dot operator.
586          We want to translate this entire paragraph as one string, of course.
587          */
588
589         //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
590
591         if ($unresolved)
592             $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.",
593                 "<<<<<<< " . _("Your version"),
594                 ">>>>>>> " . _("Other version")));
595         else
596             $message = HTML::p(_("Please check it through before saving."));
597
598         /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
599           HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
600                        $re_edit_link)),
601           HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
602           HTML::li(_("Save your updated changes.")));
603         */
604         return
605             HTML(HTML::h2(_("Conflicting Edits!")),
606                 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
607                 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.")),
608                 $message);
609     }
610
611     function getTextArea()
612     {
613         $request = &$this->request;
614
615         $readonly = !$this->canEdit(); // || $this->isConcurrentUpdate();
616
617         // WYSIWYG will need two pagetypes: raw wikitest and converted html
618         if (ENABLE_WYSIWYG) {
619             $this->_wikicontent = $this->_content;
620             $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
621             //                $this->getPreview();
622             //$this->_htmlcontent = $this->_content->asXML();
623         }
624
625         $textarea = HTML::textarea(array('class' => 'wikiedit',
626                 'name' => 'edit[content]',
627                 'id' => 'edit-content',
628                 'rows' => $request->getPref('editHeight'),
629                 'cols' => $request->getPref('editWidth'),
630                 'readonly' => (bool)$readonly),
631             $this->_content);
632         if (ENABLE_WYSIWYG) {
633             return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
634                 $textarea->getAttr('name'));
635         } else
636             return $textarea;
637     }
638
639     function getFormElements()
640     {
641         global $WikiTheme;
642         $request = &$this->request;
643         $page = &$this->page;
644
645         $h = array('action' => 'edit',
646             'pagename' => $page->getName(),
647             'version' => $this->version,
648             'edit[pagetype]' => $this->meta['pagetype'],
649             'edit[current_version]' => $this->_currentVersion);
650
651         $el['HIDDEN_INPUTS'] = HiddenInputs($h);
652         $el['EDIT_TEXTAREA'] = $this->getTextArea();
653         if (ENABLE_CAPTCHA) {
654             $el = array_merge($el, $this->Captcha->getFormElements());
655         }
656         $el['SUMMARY_INPUT']
657             = HTML::input(array('type' => 'text',
658             'class' => 'wikitext',
659             'id' => 'edit-summary',
660             'name' => 'edit[summary]',
661             'size' => 50,
662             'maxlength' => 256,
663             'value' => $this->meta['summary']));
664         $el['MINOR_EDIT_CB']
665             = HTML::input(array('type' => 'checkbox',
666             'name' => 'edit[minor_edit]',
667             'id' => 'edit-minor_edit',
668             'checked' => (bool)$this->meta['is_minor_edit']));
669         $el['OLD_MARKUP_CB']
670             = HTML::input(array('type' => 'checkbox',
671             'name' => 'edit[markup]',
672             'value' => 'old',
673             'checked' => $this->meta['markup'] < 2.0,
674             'id' => 'useOldMarkup',
675             'onclick' => 'showOldMarkupRules(this.checked)'));
676         $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0)
677             ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
678         $el['LOCKED_CB']
679             = HTML::input(array('type' => 'checkbox',
680             'name' => 'edit[locked]',
681             'id' => 'edit-locked',
682             'disabled' => (bool)!$this->user->isAdmin(),
683             'checked' => (bool)$this->locked));
684         if (ENABLE_PAGE_PUBLIC) {
685             $el['PUBLIC_CB']
686                 = HTML::input(array('type' => 'checkbox',
687                 'name' => 'edit[public]',
688                 'id' => 'edit-public',
689                 'disabled' => (bool)!$this->user->isAdmin(),
690                 'checked' => (bool)$this->page->get('public')));
691         }
692         if (ENABLE_EXTERNAL_PAGES) {
693             $el['EXTERNAL_CB']
694                 = HTML::input(array('type' => 'checkbox',
695                 'name' => 'edit[external]',
696                 'id' => 'edit-external',
697                 'disabled' => (bool)!$this->user->isAdmin(),
698                 'checked' => (bool)$this->page->get('external')));
699         }
700         if (ENABLE_WYSIWYG) {
701             if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
702                 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
703             }
704         }
705
706         $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
707             'wikiaction',
708             array('accesskey' => 'p',
709                 'title' => 'Preview the current content [alt-p]'));
710
711         //if (!$this->isConcurrentUpdate() && $this->canEdit())
712         $el['SAVE_B'] = Button('submit:edit[save]',
713             _("Save"), 'wikiaction',
714             array('accesskey' => 's',
715                 'title' => 'Save the current content as wikipage [alt-s]'));
716         $el['CHANGES_B'] = Button('submit:edit[diff]',
717             _("Changes"), 'wikiaction',
718             array('accesskey' => 'c',
719                 'title' => 'Preview the current changes as diff [alt-c]'));
720         $el['UPLOAD_B'] = Button('submit:edit[upload]',
721             _("Upload"), 'wikiaction',
722             array('title' => 'Select a local file and press Upload to attach into this page'));
723         $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
724             _("Spell Check"), 'wikiaction',
725             array('title' => 'Check the spelling'));
726         $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
727
728         $el['WIDTH_PREF']
729             = HTML::input(array('type' => 'text',
730             'size' => 3,
731             'maxlength' => 4,
732             'class' => "numeric",
733             'name' => 'pref[editWidth]',
734             'id' => 'pref-editWidth',
735             'value' => $request->getPref('editWidth'),
736             'onchange' => 'this.form.submit();'));
737         $el['HEIGHT_PREF']
738             = HTML::input(array('type' => 'text',
739             'size' => 3,
740             'maxlength' => 4,
741             'class' => "numeric",
742             'name' => 'pref[editHeight]',
743             'id' => 'pref-editHeight',
744             'value' => $request->getPref('editHeight'),
745             'onchange' => 'this.form.submit();'));
746         $el['SEP'] = $WikiTheme->getButtonSeparator();
747         $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
748             HTML::em($this->user->getId()));
749
750         return $el;
751     }
752
753     function _redirectToBrowsePage()
754     {
755         $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
756     }
757
758     function _restoreState()
759     {
760         $request = &$this->request;
761
762         $posted = $request->getArg('edit');
763         $request->setArg('edit', false);
764
765         if (!$posted
766             || !$request->isPost()
767             || !in_array($request->getArg('action'), array('edit', 'loadfile'))
768         )
769             return false;
770
771         if (!isset($posted['content']) || !is_string($posted['content']))
772             return false;
773         $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
774             rtrim($posted['content']));
775         $this->_content = $this->getContent();
776
777         $this->_currentVersion = (int)$posted['current_version'];
778
779         if ($this->_currentVersion < 0)
780             return false;
781         if ($this->_currentVersion > $this->current->getVersion())
782             return false; // FIXME: some kind of warning?
783
784         $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
785         $meta['markup'] = $is_old_markup ? false : 2.0;
786         $meta['summary'] = trim(substr($posted['summary'], 0, 256));
787         $meta['is_minor_edit'] = !empty($posted['minor_edit']);
788         $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
789         if (ENABLE_CAPTCHA)
790             $meta['captcha_input'] = !empty($posted['captcha_input']) ?
791                 $posted['captcha_input'] : '';
792
793         $this->meta = array_merge($this->meta, $meta);
794         $this->locked = !empty($posted['locked']);
795         if (ENABLE_PAGE_PUBLIC)
796             $this->public = !empty($posted['public']);
797         if (ENABLE_EXTERNAL_PAGES)
798             $this->external = !empty($posted['external']);
799
800         foreach (array('preview', 'save', 'edit_convert',
801                      'keep_old', 'overwrite', 'diff', 'upload') as $o) {
802             if (!empty($posted[$o]))
803                 $this->editaction = $o;
804         }
805         if (empty($this->editaction))
806             $this->editaction = 'edit';
807
808         return true;
809     }
810
811     function _initializeState()
812     {
813         $request = &$this->request;
814         $current = &$this->current;
815         $selected = &$this->selected;
816         $user = &$this->user;
817
818         if (!$selected)
819             NoSuchRevision($request, $this->page, $this->version); // noreturn
820
821         $this->_currentVersion = $current->getVersion();
822         $this->_content = $selected->getPackedContent();
823
824         $this->locked = $this->page->get('locked');
825
826         // If author same as previous author, default minor_edit to on.
827         $age = $this->meta['mtime'] - $current->get('mtime');
828         $this->meta['is_minor_edit'] = ($age < MINOR_EDIT_TIMEOUT
829             && $current->get('author') == $user->getId()
830         );
831
832         // Default for new pages is new-style markup.
833         if ($selected->hasDefaultContents())
834             $is_new_markup = true;
835         else
836             $is_new_markup = $selected->get('markup') >= 2.0;
837
838         $this->meta['markup'] = $is_new_markup ? 2.0 : false;
839         $this->meta['pagetype'] = $selected->get('pagetype');
840         if ($this->meta['pagetype'] == 'wikiblog')
841             $this->meta['summary'] = $selected->get('summary'); // keep blog title
842         else
843             $this->meta['summary'] = '';
844         $this->editaction = 'edit';
845     }
846 }
847
848 class LoadFileConflictPageEditor
849     extends PageEditor
850 {
851     function editPage($saveFailed = true)
852     {
853         $tokens = &$this->tokens;
854
855         if (!$this->canEdit()) {
856             if ($this->isInitialEdit()) {
857                 return $this->viewSource();
858             }
859             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
860         } elseif ($this->editaction == 'save') {
861             if ($this->savePage()) {
862                 return true; // Page saved.
863             }
864             $saveFailed = true;
865         }
866
867         if ($saveFailed || $this->isConcurrentUpdate()) {
868             // Get the text of the original page, and the two conflicting edits
869             // The diff class takes arrays as input.  So retrieve content as
870             // an array, or convert it as necesary.
871             $orig = $this->page->getRevision($this->_currentVersion);
872             $this_content = explode("\n", $this->_content);
873             $other_content = $this->current->getContent();
874             require_once 'lib/diff.php';
875             $diff2 = new Diff($other_content, $this_content);
876             $context_lines = max(4, count($other_content) + 1,
877                 count($this_content) + 1);
878             $fmt = new BlockDiffFormatter($context_lines);
879
880             $this->_content = $fmt->format($diff2);
881             // FIXME: integrate this into class BlockDiffFormatter
882             $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
883                 $this->_content);
884             $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
885                 $this->_content);
886
887             $this->_currentVersion = $this->current->getVersion();
888             $this->version = $this->_currentVersion;
889             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
890         }
891
892         if ($this->editaction == 'edit_convert')
893             $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
894         if ($this->editaction == 'preview')
895             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
896
897         // FIXME: NOT_CURRENT_MESSAGE?
898         $tokens = array_merge($tokens, $this->getFormElements());
899         // we need all GET params for loadfile overwrite
900         if ($this->request->getArg('action') == 'loadfile') {
901
902             $this->tokens['HIDDEN_INPUTS'] =
903                 HTML(HiddenInputs
904                     (array('source' => $this->request->getArg('source'),
905                         'merge' => 1)),
906                     $this->tokens['HIDDEN_INPUTS']);
907             // add two conflict resolution buttons before preview and save.
908             $tokens['PREVIEW_B'] = HTML(
909                 Button('submit:edit[keep_old]',
910                     _("Keep old"), 'wikiaction'),
911                 $tokens['SEP'],
912                 Button('submit:edit[overwrite]',
913                     _("Overwrite with new"), 'wikiaction'),
914                 $tokens['SEP'],
915                 $tokens['PREVIEW_B']);
916         }
917         if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
918             include_once 'lib/EditToolbar.php';
919             $toolbar = new EditToolbar();
920             $tokens = array_merge($tokens, $toolbar->getTokens());
921         }
922
923         return $this->output('editpage', _("Merge and Edit: %s"));
924     }
925
926     function output($template, $title_fs)
927     {
928         $selected = &$this->selected;
929         $current = &$this->current;
930
931         if ($selected && $selected->getVersion() != $current->getVersion()) {
932             $rev = $selected;
933             $pagelink = WikiLink($selected);
934         } else {
935             $rev = $current;
936             $pagelink = WikiLink($this->page);
937         }
938
939         $title = new FormattedText ($title_fs, $pagelink);
940         $this->tokens['HEADER'] = $title;
941         //hack! there's no TITLE in editpage, but in the previous top template
942         if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
943             $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
944         else
945             $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
946                 $this->tokens['PAGE_LOCKED_MESSAGE']);
947         $template = Template($template, $this->tokens);
948
949         //GeneratePage($template, $title, $rev);
950         PrintXML($template);
951         return true;
952     }
953
954     function getConflictMessage($unresolved = false)
955     {
956         $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.",
957                 "<<<<<<<",
958                 "======="),
959             HTML::p(_("Please check it through before saving."))));
960         return $message;
961     }
962 }
963
964 // Local Variables:
965 // mode: php
966 // tab-width: 8
967 // c-basic-offset: 4
968 // c-hanging-comment-ender-p: nil
969 // indent-tabs-mode: nil
970 // End: