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