]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
move Captcha specific vars and methods into a Captcha object
[SourceForge/phpwiki.git] / lib / editpage.php
1 <?php
2 rcs_id('$Id: editpage.php,v 1.100 2005-10-30 14:20:42 rurban Exp $');
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         
23         $this->meta = array('author' => $this->user->getId(),
24                             'author_id' => $this->user->getAuthenticatedId(),
25                             'mtime' => time());
26         
27         $this->tokens = array();
28
29         if (ENABLE_WYSIWYG) {
30             require_once("lib/WysiwygEdit.php");
31             if (USE_TINYMCE)       $backend = "tinymce";
32             elseif (USE_HTMLAREA3) $backend = "htmlarea3";
33             elseif (USE_HTMLAREA2) $backend = "htmlarea2";
34             else trigger_error("undefined WysiwygEdit backend", E_USER_ERROR);
35
36             require_once("lib/WysiwygEdit/$backend.php");
37             $class = "WysiwygEdit_$backend";
38             $this->WysiwygEdit = new $class();
39         }
40         if (ENABLE_CAPTCHA) {
41             require_once('lib/Captcha.php');
42             $this->Captcha = new Captcha($this->meta);
43         }
44
45         $version = $request->getArg('version');
46         if ($version !== false) {
47             $this->selected = $this->page->getRevision($version);
48             $this->version = $version;
49         }
50         else {
51             $this->version = $this->current->getVersion();
52             $this->selected = $this->page->getRevision($this->version);
53         }
54
55         if ($this->_restoreState()) {
56             $this->_initialEdit = false;
57         }
58         else {
59             $this->_initializeState();
60             $this->_initialEdit = true;
61
62             // The edit request has specified some initial content from a template 
63             if (  ($template = $request->getArg('template'))
64                    and $request->_dbi->isWikiPage($template)) {
65                 $page = $request->_dbi->getPage($template);
66                 $current = $page->getCurrentRevision();
67                 $this->_content = $current->getPackedContent();
68             } elseif ($initial_content = $request->getArg('initial_content')) {
69                 $this->_content = $initial_content;
70                 $this->_redirect_to = $request->getArg('save_and_redirect_to');
71             }
72         }
73         if (!headers_sent())
74             header("Content-Type: text/html; charset=" . $GLOBALS['charset']);
75     }
76
77     function editPage () {
78         global $WikiTheme;
79         $saveFailed = false;
80         $tokens = &$this->tokens;
81         $tokens['PAGE_LOCKED_MESSAGE'] = '';
82         $tokens['CONCURRENT_UPDATE_MESSAGE'] = '';
83
84         if (isset($this->request->args['pref']['editWidth'])
85             and ($this->request->getPref('editWidth') != $this->request->args['pref']['editWidth'])) {
86             $this->request->_prefs->set('editWidth', $this->request->args['pref']['editWidth']);
87         }
88         if (isset($this->request->args['pref']['editHeight'])
89             and ($this->request->getPref('editHeight') != $this->request->args['pref']['editHeight'])) {
90             $this->request->_prefs->set('editHeight', $this->request->args['pref']['editHeight']);
91         }
92
93         if (! $this->canEdit()) {
94             if ($this->isInitialEdit())
95                 return $this->viewSource();
96             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
97         }
98         elseif ($this->request->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             }
103             elseif ( $this->savePage()) {
104                 // noreturn
105                 $this->request->redirect(WikiURL($this->request->getArg('save_and_redirect_to')));
106                 return true;    // Page saved.
107             }
108             $saveFailed = true;
109         }
110         elseif ($this->editaction == 'save') {
111             if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
112                 $this->tokens['PAGE_LOCKED_MESSAGE'] = 
113                     HTML::p(HTML::h1($this->Captcha->failed_msg));
114             }
115             elseif ($this->savePage()) {
116                 return true;    // Page saved.
117             }
118             else {
119                 $saveFailed = true;
120             }
121         }
122
123         if ($saveFailed and $this->isConcurrentUpdate())
124         {
125             // Get the text of the original page, and the two conflicting edits
126             // The diff3 class takes arrays as input.  So retrieve content as
127             // an array, or convert it as necesary.
128             $orig = $this->page->getRevision($this->_currentVersion);
129             // FIXME: what if _currentVersion has be deleted?
130             $orig_content = $orig->getContent();
131             $this_content = explode("\n", $this->_content);
132             $other_content = $this->current->getContent();
133             include_once("lib/diff3.php");
134             $diff = new diff3($orig_content, $this_content, $other_content);
135             $output = $diff->merged_output(_("Your version"), _("Other version"));
136             // Set the content of the textarea to the merged diff
137             // output, and update the version
138             $this->_content = implode ("\n", $output);
139             $this->_currentVersion = $this->current->getVersion();
140             $this->version = $this->_currentVersion;
141             $unresolved = $diff->ConflictingBlocks;
142             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage($unresolved);
143         } elseif ($saveFailed && !$this->_isSpam) {
144             $tokens['CONCURRENT_UPDATE_MESSAGE'] = 
145                 HTML(HTML::h2(_("Some internal editing error")),
146                      HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
147                      HTML::p(HTML::em(_("&version=-1 might help."))));
148         }
149
150         if ($this->editaction == 'edit_convert')
151             $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
152         if ($this->editaction == 'preview')
153             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
154
155         // FIXME: NOT_CURRENT_MESSAGE?
156         $tokens = array_merge($tokens, $this->getFormElements());
157
158         if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
159             include_once("lib/EditToolbar.php");
160             $toolbar = new EditToolbar();
161             $tokens = array_merge($tokens, $toolbar->getTokens());
162         }
163
164         return $this->output('editpage', _("Edit: %s"));
165     }
166
167     function output ($template, $title_fs) {
168         global $WikiTheme;
169         $selected = &$this->selected;
170         $current = &$this->current;
171
172         if ($selected && $selected->getVersion() != $current->getVersion()) {
173             $rev = $selected;
174             $pagelink = WikiLink($selected);
175         }
176         else {
177             $rev = $current;
178             $pagelink = WikiLink($this->page);
179         }
180
181         $title = new FormattedText ($title_fs, $pagelink);
182         // not for dumphtml or viewsource
183         if (ENABLE_WYSIWYG and $template == 'editpage') {
184             $WikiTheme->addMoreHeaders($this->WysiwygEdit->Head());
185             //$tokens['PAGE_SOURCE'] = $WysiwygEdit->ConvertBefore($this->_content);
186         }
187         $template = Template($template, $this->tokens);
188         GeneratePage($template, $title, $rev);
189         return true;
190     }
191
192
193     function viewSource () {
194         assert($this->isInitialEdit());
195         assert($this->selected);
196
197         $this->tokens['PAGE_SOURCE'] = $this->_content;
198         return $this->output('viewsource', _("View Source: %s"));
199     }
200
201     function updateLock() {
202         if ((bool)$this->page->get('locked') == (bool)$this->locked)
203             return false;       // Not changed.
204
205         if (!$this->user->isAdmin()) {
206             // FIXME: some sort of message
207             return false;         // not allowed.
208         }
209
210         $this->page->set('locked', (bool)$this->locked);
211         $this->tokens['LOCK_CHANGED_MSG']
212             = $this->locked ? _("Page now locked.") : _("Page now unlocked.");
213
214         return true;            // lock changed.
215     }
216
217     function savePage () {
218         $request = &$this->request;
219
220         if ($this->isUnchanged()) {
221             // Allow admin lock/unlock even if
222             // no text changes were made.
223             if ($this->updateLock()) {
224                 $dbi = $request->getDbh();
225                 $dbi->touch();
226             }
227             // Save failed. No changes made.
228             $this->_redirectToBrowsePage();
229             // user will probably not see the rest of this...
230             include_once('lib/display.php');
231             // force browse of current version:
232             $request->setArg('version', false);
233             displayPage($request, 'nochanges');
234             return true;
235         }
236
237         if ($this->isSpam()) {
238             $this->_isSpam = true;
239             return false;
240             /*
241             // Save failed. No changes made.
242             $this->_redirectToBrowsePage();
243             // user will probably not see the rest of this...
244             include_once('lib/display.php');
245             // force browse of current version:
246             $request->setArg('version', false);
247             displayPage($request, 'nochanges');
248             return true;
249             */
250         }
251
252         $page = &$this->page;
253
254         // Include any meta-data from original page version which
255         // has not been explicitly updated.
256         // (Except don't propagate pgsrc_version --- moot for now,
257         //  because at present it never gets into the db...)
258         $meta = $this->selected->getMetaData();
259         unset($meta['pgsrc_version']);
260         $meta = array_merge($meta, $this->meta);
261         
262         // Save new revision
263         $this->_content = $this->getContent();
264         $newrevision = $page->save($this->_content, 
265                                    $this->version == -1 
266                                      ? -1 
267                                      : $this->_currentVersion + 1, 
268                                    // force new?
269                                    $meta);
270         if (!isa($newrevision, 'WikiDB_PageRevision')) {
271             // Save failed.  (Concurrent updates).
272             return false;
273         }
274         
275         // New contents successfully saved...
276         $this->updateLock();
277
278         // Clean out archived versions of this page.
279         include_once('lib/ArchiveCleaner.php');
280         $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
281         $cleaner->cleanPageRevisions($page);
282
283         /* generate notification emails done in WikiDB::save to catch all direct calls 
284           (admin plugins) */
285
286         // look at the errorstack
287         $errors   = $GLOBALS['ErrorManager']->_postponed_errors;
288         $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML(); 
289         $GLOBALS['ErrorManager']->_postponed_errors = $errors;
290
291         $dbi = $request->getDbh();
292         $dbi->touch();
293         
294         global $WikiTheme;
295         if (empty($warnings->_content) && ! $WikiTheme->getImageURL('signature')) {
296             // Do redirect to browse page if no signature has
297             // been defined.  In this case, the user will most
298             // likely not see the rest of the HTML we generate
299             // (below).
300             $this->_redirectToBrowsePage();
301         }
302
303         // Force browse of current page version.
304         $request->setArg('version', false);
305         //$request->setArg('action', false);
306
307         $template = Template('savepage', $this->tokens);
308         $template->replace('CONTENT', $newrevision->getTransformedContent());
309         if (!empty($warnings->_content))
310             $template->replace('WARNINGS', $warnings);
311
312         $pagelink = WikiLink($page);
313
314         GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
315         return true;
316     }
317
318     function isConcurrentUpdate () {
319         assert($this->current->getVersion() >= $this->_currentVersion);
320         return $this->current->getVersion() != $this->_currentVersion;
321     }
322
323     function canEdit () {
324         return !$this->page->get('locked') || $this->user->isAdmin();
325     }
326
327     function isInitialEdit () {
328         return $this->_initialEdit;
329     }
330
331     function isUnchanged () {
332         $current = &$this->current;
333
334         if ($this->meta['markup'] !=  $current->get('markup'))
335             return false;
336
337         return $this->_content == $current->getPackedContent();
338     }
339
340     /** 
341      * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
342      * Need to check dynamically some blacklist wikipage settings 
343      * (plugin WikiAccessRestrictions) and some static blacklist.
344      * DONE: 
345      *   Always: More then 20 new external links
346      *   ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
347      *   ENABLE_SPAMBLOCKLIST: IP blacklist, domain blacklist, url patterns
348      */
349     function isSpam () {
350         $current = &$this->current;
351         $request = &$this->request;
352
353         $oldtext = $current->getPackedContent();
354         $newtext =& $this->_content;
355
356         // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
357         //        better use a certain text : link ratio.
358
359         // 1. Not more then 20 new external links
360         if ($this->numLinks($newtext) - $this->numLinks($oldtext) >= NUM_SPAM_LINKS) {
361             // TODO: mail the admin?
362             $this->tokens['PAGE_LOCKED_MESSAGE'] = 
363                 HTML($this->getSpamMessage(),
364                      HTML::p(HTML::strong(_("Too many external links."))));
365             return true;
366         }
367         // 2. external babycart (SpamAssassin) check
368         // This will probably prevent from discussing sex or viagra related topics. So beware.
369         if (ENABLE_SPAMASSASSIN) {
370             $user = $request->getUser();
371             include_once("lib/spam_babycart.php");
372             if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"), 
373                                            $user->getId())) {
374                 // TODO: mail the admin
375                 if (is_array($babycart))
376                     $this->tokens['PAGE_LOCKED_MESSAGE'] = 
377                         HTML($this->getSpamMessage(),
378                              HTML::p(HTML::em(_("SpamAssassin reports: ", 
379                                                 join("\n", $babycart)))));
380                 return true;
381             }
382         }
383         // 3. extract (new) links and check surbl for blocked domains
384         if (ENABLE_SPAMBLOCKLIST and $this->numLinks($newtext)) {
385             include_once("lib/SpamBlocklist.php");
386             include_once("lib/InlineParser.php");
387             $parsed = TransformLinks($newtext);
388             foreach ($parsed->_content as $link) {
389                 if (isa($link, 'Cached_ExternalLink')) {
390                   $uri = $link->_getURL($this->page->getName());
391                   if ($res = IsBlackListed($uri)) {
392                     // TODO: mail the admin
393                     $this->tokens['PAGE_LOCKED_MESSAGE'] = 
394                         HTML($this->getSpamMessage(),
395                              HTML::p(HTML::strong(_("External links contain blocked domains:")),
396                              HTML::ul(HTML::li(sprintf(_("%s is listed at %s"), 
397                                                        $res[2], $res[0])))));
398                     return true;
399                   }
400                 }
401             }
402         }
403
404         return false;
405     }
406
407     /** Number of external links in the wikitext
408      */
409     function numLinks(&$text) {
410         return substr_count($text, "http://") + substr_count($text, "https://");
411     }
412
413     /** Header of the Anti Spam message 
414      */
415     function getSpamMessage () {
416         return
417             HTML(HTML::h2(_("Spam Prevention")),
418                  HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
419                          HTML::br(),
420                          _("Sorry for the inconvenience.")),
421                  HTML::p(""));
422     }
423
424     function getPreview () {
425         include_once('lib/PageType.php');
426         $this->_content = $this->getContent();
427         return new TransformedText($this->page, $this->_content, $this->meta);
428     }
429
430     function getConvertedPreview () {
431         include_once('lib/PageType.php');
432         $this->_content = $this->getContent();
433         $this->meta['markup'] = 2.0;
434         $this->_content = ConvertOldMarkup($this->_content);
435         return new TransformedText($this->page, $this->_content, $this->meta);
436     }
437
438     // possibly convert HTMLAREA content back to Wiki markup
439     function getContent () {
440         if (ENABLE_WYSIWYG) {
441             $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
442             //$this->_content = join("", $xml_output->_content);
443             return $this->_content;
444         } else {
445             return $this->_content;
446         }
447     }
448
449     function getLockedMessage () {
450         return
451             HTML(HTML::h2(_("Page Locked")),
452                  HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
453                  HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
454                  HTML::p(_("Sorry for the inconvenience.")));
455     }
456
457     function getConflictMessage ($unresolved = false) {
458         /*
459          xgettext only knows about c/c++ line-continuation strings
460          it does not know about php's dot operator.
461          We want to translate this entire paragraph as one string, of course.
462          */
463
464         //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
465
466         if ($unresolved)
467             $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.",
468                                 "<<<<<<< ". _("Your version"),
469                                 ">>>>>>> ". _("Other version")));
470         else
471             $message = HTML::p(_("Please check it through before saving."));
472
473
474
475         /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
476           HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
477                        $re_edit_link)),
478           HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
479           HTML::li(_("Save your updated changes.")));
480         */
481         return
482             HTML(HTML::h2(_("Conflicting Edits!")),
483                  HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
484                  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.")),
485                  $message);
486     }
487
488
489     function getTextArea () {
490         $request = &$this->request;
491
492         $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
493
494         // WYSIWYG will need two pagetypes: raw wikitest and converted html
495         if (ENABLE_WYSIWYG) {
496             $this->_wikicontent = $this->_content;
497             $this->_content = $this->getPreview();
498             // $html = $this->getPreview();
499             // $this->_content = $html->asXML();
500         }
501
502         $textarea = HTML::textarea(array('class'=> 'wikiedit',
503                                          'name' => 'edit[content]',
504                                          'id'   => 'edit:content',
505                                          'rows' => $request->getPref('editHeight'),
506                                          'cols' => $request->getPref('editWidth'),
507                                          'readonly' => (bool) $readonly),
508                                    $this->_content);
509         /** <textarea wrap="virtual"> is not valid xhtml but Netscape 4 requires it
510          * to wrap long lines.
511          */
512         if (isBrowserNS4())
513             $textarea->setAttr('wrap', 'virtual');
514         if (ENABLE_WYSIWYG) {
515             return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent, 
516                                                 $textarea->getAttr('name'));
517         } else
518             return $textarea;
519     }
520
521     function getFormElements () {
522         global $WikiTheme;
523         $request = &$this->request;
524         $page = &$this->page;
525
526         $h = array('action'   => 'edit',
527                    'pagename' => $page->getName(),
528                    'version'  => $this->version,
529                    'edit[pagetype]' => $this->meta['pagetype'],
530                    'edit[current_version]' => $this->_currentVersion);
531
532         $el['HIDDEN_INPUTS'] = HiddenInputs($h);
533         $el['EDIT_TEXTAREA'] = $this->getTextArea();
534         if ( ENABLE_CAPTCHA ) {
535             $el = array_merge($el, $this->Captcha->getFormElements());
536         }
537         $el['SUMMARY_INPUT']
538             = HTML::input(array('type'  => 'text',
539                                 'class' => 'wikitext',
540                                 'id' => 'edit:summary',
541                                 'name'  => 'edit[summary]',
542                                 'size'  => 50,
543                                 'maxlength' => 256,
544                                 'value' => $this->meta['summary']));
545         $el['MINOR_EDIT_CB']
546             = HTML::input(array('type' => 'checkbox',
547                                 'name'  => 'edit[minor_edit]',
548                                 'id' => 'edit:minor_edit',
549                                 'checked' => (bool) $this->meta['is_minor_edit']));
550         $el['OLD_MARKUP_CB']
551             = HTML::input(array('type' => 'checkbox',
552                                 'name' => 'edit[markup]',
553                                 'value' => 'old',
554                                 'checked' => $this->meta['markup'] < 2.0,
555                                 'id' => 'useOldMarkup',
556                                 'onclick' => 'showOldMarkupRules(this.checked)'));
557         $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0) 
558             ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
559         $el['LOCKED_CB']
560             = HTML::input(array('type' => 'checkbox',
561                                 'name' => 'edit[locked]',
562                                 'id'   => 'edit:locked',
563                                 'disabled' => (bool) !$this->user->isadmin(),
564                                 'checked'  => (bool) $this->locked));
565
566         $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
567                                   'wikiaction');
568
569         //if (!$this->isConcurrentUpdate() && $this->canEdit())
570         $el['SAVE_B'] = Button('submit:edit[save]', _("Save"), 'wikiaction');
571
572         $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
573
574         $el['WIDTH_PREF'] 
575             = HTML::input(array('type'     => 'text',
576                                 'size'     => 3,
577                                 'maxlength'=> 4,
578                                 'class'    => "numeric",
579                                 'name'     => 'pref[editWidth]',
580                                 'id'       => 'pref:editWidth',
581                                 'value'    => $request->getPref('editWidth'),
582                                 'onchange' => 'this.form.submit();'));
583         $el['HEIGHT_PREF'] 
584             = HTML::input(array('type'     => 'text',
585                                 'size'     => 3,
586                                 'maxlength'=> 4,
587                                 'class'    => "numeric",
588                                 'name'     => 'pref[editHeight]',
589                                 'id'       => 'pref:editHeight',
590                                 'value'    => $request->getPref('editHeight'),
591                                 'onchange' => 'this.form.submit();'));
592         $el['SEP'] = $WikiTheme->getButtonSeparator();
593         $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.", 
594                                     HTML::em($this->user->getId()));
595         
596         return $el;
597     }
598
599     function _redirectToBrowsePage() {
600         $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
601     }
602
603     function _restoreState () {
604         $request = &$this->request;
605
606         $posted = $request->getArg('edit');
607         $request->setArg('edit', false);
608
609         if (!$posted || !$request->isPost()
610             || $request->getArg('action') != 'edit')
611             return false;
612
613         if (!isset($posted['content']) || !is_string($posted['content']))
614             return false;
615         $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
616                                         rtrim($posted['content']));
617         $this->_content = $this->getContent();
618
619         $this->_currentVersion = (int) $posted['current_version'];
620
621         if ($this->_currentVersion < 0)
622             return false;
623         if ($this->_currentVersion > $this->current->getVersion())
624             return false;       // FIXME: some kind of warning?
625
626         $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
627         $meta['markup'] = $is_old_markup ? false : 2.0;
628         $meta['summary'] = trim(substr($posted['summary'], 0, 256));
629         $meta['is_minor_edit'] = !empty($posted['minor_edit']);
630         $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
631         if ( ENABLE_CAPTCHA )
632             $meta['captcha_input'] = !empty($posted['captcha_input']) ?
633                 $posted['captcha_input'] : '';
634
635         $this->meta = array_merge($this->meta, $meta);
636         $this->locked = !empty($posted['locked']);
637
638         if (!empty($posted['preview']))
639             $this->editaction = 'preview';
640         elseif (!empty($posted['save']))
641             $this->editaction = 'save';
642         elseif (!empty($posted['edit_convert']))
643             $this->editaction = 'edit_convert';
644         else
645             $this->editaction = 'edit';
646
647         return true;
648     }
649
650     function _initializeState () {
651         $request = &$this->request;
652         $current = &$this->current;
653         $selected = &$this->selected;
654         $user = &$this->user;
655
656         if (!$selected)
657             NoSuchRevision($request, $this->page, $this->version); // noreturn
658
659         $this->_currentVersion = $current->getVersion();
660         $this->_content = $selected->getPackedContent();
661
662         $this->locked = $this->page->get('locked');
663
664         // If author same as previous author, default minor_edit to on.
665         $age = $this->meta['mtime'] - $current->get('mtime');
666         $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
667                                          && $current->get('author') == $user->getId()
668                                          );
669
670         // Default for new pages is new-style markup.
671         if ($selected->hasDefaultContents())
672             $is_new_markup = true;
673         else
674             $is_new_markup = $selected->get('markup') >= 2.0;
675
676         $this->meta['markup'] = $is_new_markup ? 2.0: false;
677         $this->meta['pagetype'] = $selected->get('pagetype');
678         if ($this->meta['pagetype'] == 'wikiblog')
679             $this->meta['summary'] = $selected->get('summary'); // keep blog title
680         else
681             $this->meta['summary'] = '';
682         $this->editaction = 'edit';
683     }
684 }
685
686 class LoadFileConflictPageEditor
687 extends PageEditor
688 {
689     function editPage ($saveFailed = true) {
690         $tokens = &$this->tokens;
691
692         if (!$this->canEdit()) {
693             if ($this->isInitialEdit())
694                 return $this->viewSource();
695             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
696         }
697         elseif ($this->editaction == 'save') {
698             if ($this->savePage())
699                 return true;    // Page saved.
700             $saveFailed = true;
701         }
702
703         if ($saveFailed || $this->isConcurrentUpdate())
704         {
705             // Get the text of the original page, and the two conflicting edits
706             // The diff class takes arrays as input.  So retrieve content as
707             // an array, or convert it as necesary.
708             $orig = $this->page->getRevision($this->_currentVersion);
709             $this_content = explode("\n", $this->_content);
710             $other_content = $this->current->getContent();
711             include_once("lib/diff.php");
712             $diff2 = new Diff($other_content, $this_content);
713             $context_lines = max(4, count($other_content) + 1,
714                                  count($this_content) + 1);
715             $fmt = new BlockDiffFormatter($context_lines);
716
717             $this->_content = $fmt->format($diff2);
718             // FIXME: integrate this into class BlockDiffFormatter
719             $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
720                                           $this->_content);
721             $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
722                                           $this->_content);
723
724             $this->_currentVersion = $this->current->getVersion();
725             $this->version = $this->_currentVersion;
726             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
727         }
728
729         if ($this->editaction == 'edit_convert')
730             $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
731         if ($this->editaction == 'preview')
732             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
733
734         // FIXME: NOT_CURRENT_MESSAGE?
735         $tokens = array_merge($tokens, $this->getFormElements());
736
737         return $this->output('editpage', _("Merge and Edit: %s"));
738         // FIXME: this doesn't display
739     }
740
741     function output ($template, $title_fs) {
742         $selected = &$this->selected;
743         $current = &$this->current;
744
745         if ($selected && $selected->getVersion() != $current->getVersion()) {
746             $rev = $selected;
747             $pagelink = WikiLink($selected);
748         }
749         else {
750             $rev = $current;
751             $pagelink = WikiLink($this->page);
752         }
753
754         $title = new FormattedText ($title_fs, $pagelink);
755         $template = Template($template, $this->tokens);
756
757         //GeneratePage($template, $title, $rev);
758         PrintXML($template);
759         return true;
760     }
761
762     function getConflictMessage () {
763         $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.",
764                                     "<<<<<<<",
765                                     "======="),
766                                 HTML::p(_("Please check it through before saving."))));
767         return $message;
768     }
769 }
770
771 /**
772  $Log: not supported by cvs2svn $
773  Revision 1.99  2005/10/29 08:21:58  rurban
774  ENABLE_SPAMBLOCKLIST:
775    Check for links to blocked external tld domains in new edits, against
776    multi.surbl.org and bl.spamcop.net.
777
778  Revision 1.98  2005/10/10 19:37:04  rurban
779  change USE_HTMLAREA to ENABLE WYSIWYG, add NUM_SPAM_LINKS=20
780
781  Revision 1.97  2005/09/26 06:32:22  rurban
782  [] is forbidden in id tags. Renamed to use :
783
784  Revision 1.96  2005/05/06 17:54:22  rurban
785  silence Preview warnings for PAGE_LOCKED_MESSAGE, CONCURRENT_UPDATE_MESSAGE (thanks to schorni)
786
787  Revision 1.95  2005/04/25 20:17:14  rurban
788  captcha feature by Benjamin Drieu. Patch #1110699
789
790  Revision 1.94  2005/02/28 20:23:31  rurban
791  fix error_stack
792
793  Revision 1.93  2005/02/27 19:31:52  rurban
794  hack: display errorstack without sideeffects (save and restore)
795
796  Revision 1.92  2005/01/29 20:37:21  rurban
797  no edit toolbar at all if ENABLE_EDITTOOLBAR = false
798
799  Revision 1.91  2005/01/25 07:05:49  rurban
800  extract toolbar code, support new tags to get rid of php inside templates
801
802  Revision 1.90  2005/01/22 12:46:15  rurban
803  fix oldmakrup button label
804  update pref[edit*] settings
805
806  Revision 1.89  2005/01/21 14:07:49  rurban
807  reformatting
808
809  Revision 1.88  2004/12/17 16:39:03  rurban
810  minor reformatting
811
812  Revision 1.87  2004/12/16 18:28:05  rurban
813  keep wikiblog summary = page title
814
815  Revision 1.86  2004/12/11 14:50:15  rurban
816  new edit_convert button, to get rid of old markup eventually
817
818  Revision 1.85  2004/12/06 19:49:56  rurban
819  enable action=remove which is undoable and seeable in RecentChanges: ADODB ony for now.
820  renamed delete_page to purge_page.
821  enable action=edit&version=-1 to force creation of a new version.
822  added BABYCART_PATH config
823  fixed magiqc in adodb.inc.php
824  and some more docs
825
826  Revision 1.84  2004/12/04 12:58:26  rurban
827  enable babycart Blog::SpamAssassin module on ENABLE_SPAMASSASSIN=true
828  (currently only for php >= 4.3.0)
829
830  Revision 1.83  2004/12/04 11:55:39  rurban
831  First simple AntiSpam prevention:
832    No more than 20 new http:// links allowed
833
834  Revision 1.82  2004/11/30 22:21:56  rurban
835  changed gif to optimized (pngout) png
836
837  Revision 1.81  2004/11/29 17:57:27  rurban
838  translated pulldown buttons
839
840  Revision 1.80  2004/11/25 17:20:51  rurban
841  and again a couple of more native db args: backlinks
842
843  Revision 1.79  2004/11/21 11:59:20  rurban
844  remove final \n to be ob_cache independent
845
846  Revision 1.78  2004/11/16 17:57:45  rurban
847  fix search&replace button
848  use new addTagButton machinery
849  new showPulldown for categories, TODO: in a seperate request
850
851  Revision 1.77  2004/11/15 15:52:35  rurban
852  improve js stability
853
854  Revision 1.76  2004/11/15 15:37:34  rurban
855  fix JS_SEARCHREPLACE
856    don't use document.write for replace, otherwise self.opener is not defined.
857
858  Revision 1.75  2004/09/16 08:00:52  rurban
859  just some comments
860
861  Revision 1.74  2004/07/03 07:36:28  rurban
862  do not get unneccessary content
863
864  Revision 1.73  2004/06/16 21:23:44  rurban
865  fixed non-object fatal #215
866
867  Revision 1.72  2004/06/14 11:31:37  rurban
868  renamed global $Theme to $WikiTheme (gforge nameclash)
869  inherit PageList default options from PageList
870    default sortby=pagename
871  use options in PageList_Selectable (limit, sortby, ...)
872  added action revert, with button at action=diff
873  added option regex to WikiAdminSearchReplace
874
875  Revision 1.71  2004/06/03 18:06:29  rurban
876  fix file locking issues (only needed on write)
877  fixed immediate LANG and THEME in-session updates if not stored in prefs
878  advanced editpage toolbars (search & replace broken)
879
880  Revision 1.70  2004/06/02 20:47:47  rurban
881  dont use the wikiaction class
882
883  Revision 1.69  2004/06/02 10:17:56  rurban
884  integrated search/replace into toolbar
885  added save+preview buttons
886
887  Revision 1.68  2004/06/01 15:28:00  rurban
888  AdminUser only ADMIN_USER not member of Administrators
889  some RateIt improvements by dfrankow
890  edit_toolbar buttons
891
892  Revision _1.6  2004/05/26 15:48:00  syilek
893  fixed problem with creating page with slashes from one true page
894
895  Revision _1.5  2004/05/25 16:51:53  syilek
896  added ability to create a page from the category page and not have to edit it
897
898  Revision 1.67  2004/05/27 17:49:06  rurban
899  renamed DB_Session to DbSession (in CVS also)
900  added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
901  remove leading slash in error message
902  added force_unlock parameter to File_Passwd (no return on stale locks)
903  fixed adodb session AffectedRows
904  added FileFinder helpers to unify local filenames and DATA_PATH names
905  editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
906
907  Revision 1.66  2004/04/29 23:25:12  rurban
908  re-ordered locale init (as in 1.3.9)
909  fixed loadfile with subpages, and merge/restore anyway
910    (sf.net bug #844188)
911
912  Revision 1.65  2004/04/18 01:11:52  rurban
913  more numeric pagename fixes.
914  fixed action=upload with merge conflict warnings.
915  charset changed from constant to global (dynamic utf-8 switching)
916
917  Revision 1.64  2004/04/06 19:48:56  rurban
918  temp workaround for action=edit AddComment form
919
920  Revision 1.63  2004/03/24 19:39:02  rurban
921  php5 workaround code (plus some interim debugging code in XmlElement)
922    php5 doesn't work yet with the current XmlElement class constructors,
923    WikiUserNew does work better than php4.
924  rewrote WikiUserNew user upgrading to ease php5 update
925  fixed pref handling in WikiUserNew
926  added Email Notification
927  added simple Email verification
928  removed emailVerify userpref subclass: just a email property
929  changed pref binary storage layout: numarray => hash of non default values
930  print optimize message only if really done.
931  forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string.
932    prefs should be stored in db or homepage, besides the current session.
933
934  Revision 1.62  2004/03/17 18:41:05  rurban
935  initial_content and template support for CreatePage
936
937  Revision 1.61  2004/03/12 20:59:17  rurban
938  important cookie fix by Konstantin Zadorozhny
939  new editpage feature: JS_SEARCHREPLACE
940
941  Revision 1.60  2004/02/15 21:34:37  rurban
942  PageList enhanced and improved.
943  fixed new WikiAdmin... plugins
944  editpage, Theme with exp. htmlarea framework
945    (htmlarea yet committed, this is really questionable)
946  WikiUser... code with better session handling for prefs
947  enhanced UserPreferences (again)
948  RecentChanges for show_deleted: how should pages be deleted then?
949
950  Revision 1.59  2003/12/07 20:35:26  carstenklapp
951  Bugfix: Concurrent updates broken since after 1.3.4 release: Fatal
952  error: Call to undefined function: gettransformedcontent() in
953  /home/groups/p/ph/phpwiki/htdocs/phpwiki2/lib/editpage.php on line
954  205.
955
956  Revision 1.58  2003/03/10 18:25:22  dairiki
957  Bug/typo fix.  If you use the edit page to un/lock a page, it
958  failed with: Fatal error: Call to a member function on a
959  non-object in editpage.php on line 136
960
961  Revision 1.57  2003/02/26 03:40:22  dairiki
962  New action=create.  Essentially the same as action=edit, except that if the
963  page already exists, it falls back to action=browse.
964
965  This is for use in the "question mark" links for unknown wiki words
966  to avoid problems and confusion when following links from stale pages.
967  (If the "unknown page" has been created in the interim, the user probably
968  wants to view the page before editing it.)
969
970  Revision 1.56  2003/02/21 18:07:14  dairiki
971  Minor, nitpicky, currently inconsequential changes.
972
973  Revision 1.55  2003/02/21 04:10:58  dairiki
974  Fixes for new cached markup.
975  Some minor code cleanups.
976
977  Revision 1.54  2003/02/16 19:47:16  dairiki
978  Update WikiDB timestamp when editing or deleting pages.
979
980  Revision 1.53  2003/02/15 23:20:27  dairiki
981  Redirect back to browse current version of page upon save,
982  even when no changes were made.
983
984  Revision 1.52  2003/01/03 22:22:00  carstenklapp
985  Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
986
987  Revision 1.51  2003/01/03 02:43:26  carstenklapp
988  New class LoadFileConflictPageEditor, for merging / comparing a loaded
989  pgsrc file with an existing page.
990
991  */
992
993 // Local Variables:
994 // mode: php
995 // tab-width: 8
996 // c-basic-offset: 4
997 // c-hanging-comment-ender-p: nil
998 // indent-tabs-mode: nil
999 // End:
1000 ?>