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