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