]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
hack: display errorstack without sideeffects (save and restore)
[SourceForge/phpwiki.git] / lib / editpage.php
1 <?php
2 rcs_id('$Id: editpage.php,v 1.93 2005-02-27 19:31:52 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 = $this->_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.92  2005/01/29 20:37:21  rurban
718  no edit toolbar at all if ENABLE_EDITTOOLBAR = false
719
720  Revision 1.91  2005/01/25 07:05:49  rurban
721  extract toolbar code, support new tags to get rid of php inside templates
722
723  Revision 1.90  2005/01/22 12:46:15  rurban
724  fix oldmakrup button label
725  update pref[edit*] settings
726
727  Revision 1.89  2005/01/21 14:07:49  rurban
728  reformatting
729
730  Revision 1.88  2004/12/17 16:39:03  rurban
731  minor reformatting
732
733  Revision 1.87  2004/12/16 18:28:05  rurban
734  keep wikiblog summary = page title
735
736  Revision 1.86  2004/12/11 14:50:15  rurban
737  new edit_convert button, to get rid of old markup eventually
738
739  Revision 1.85  2004/12/06 19:49:56  rurban
740  enable action=remove which is undoable and seeable in RecentChanges: ADODB ony for now.
741  renamed delete_page to purge_page.
742  enable action=edit&version=-1 to force creation of a new version.
743  added BABYCART_PATH config
744  fixed magiqc in adodb.inc.php
745  and some more docs
746
747  Revision 1.84  2004/12/04 12:58:26  rurban
748  enable babycart Blog::SpamAssassin module on ENABLE_SPAMASSASSIN=true
749  (currently only for php >= 4.3.0)
750
751  Revision 1.83  2004/12/04 11:55:39  rurban
752  First simple AntiSpam prevention:
753    No more than 20 new http:// links allowed
754
755  Revision 1.82  2004/11/30 22:21:56  rurban
756  changed gif to optimized (pngout) png
757
758  Revision 1.81  2004/11/29 17:57:27  rurban
759  translated pulldown buttons
760
761  Revision 1.80  2004/11/25 17:20:51  rurban
762  and again a couple of more native db args: backlinks
763
764  Revision 1.79  2004/11/21 11:59:20  rurban
765  remove final \n to be ob_cache independent
766
767  Revision 1.78  2004/11/16 17:57:45  rurban
768  fix search&replace button
769  use new addTagButton machinery
770  new showPulldown for categories, TODO: in a seperate request
771
772  Revision 1.77  2004/11/15 15:52:35  rurban
773  improve js stability
774
775  Revision 1.76  2004/11/15 15:37:34  rurban
776  fix JS_SEARCHREPLACE
777    don't use document.write for replace, otherwise self.opener is not defined.
778
779  Revision 1.75  2004/09/16 08:00:52  rurban
780  just some comments
781
782  Revision 1.74  2004/07/03 07:36:28  rurban
783  do not get unneccessary content
784
785  Revision 1.73  2004/06/16 21:23:44  rurban
786  fixed non-object fatal #215
787
788  Revision 1.72  2004/06/14 11:31:37  rurban
789  renamed global $Theme to $WikiTheme (gforge nameclash)
790  inherit PageList default options from PageList
791    default sortby=pagename
792  use options in PageList_Selectable (limit, sortby, ...)
793  added action revert, with button at action=diff
794  added option regex to WikiAdminSearchReplace
795
796  Revision 1.71  2004/06/03 18:06:29  rurban
797  fix file locking issues (only needed on write)
798  fixed immediate LANG and THEME in-session updates if not stored in prefs
799  advanced editpage toolbars (search & replace broken)
800
801  Revision 1.70  2004/06/02 20:47:47  rurban
802  dont use the wikiaction class
803
804  Revision 1.69  2004/06/02 10:17:56  rurban
805  integrated search/replace into toolbar
806  added save+preview buttons
807
808  Revision 1.68  2004/06/01 15:28:00  rurban
809  AdminUser only ADMIN_USER not member of Administrators
810  some RateIt improvements by dfrankow
811  edit_toolbar buttons
812
813  Revision _1.6  2004/05/26 15:48:00  syilek
814  fixed problem with creating page with slashes from one true page
815
816  Revision _1.5  2004/05/25 16:51:53  syilek
817  added ability to create a page from the category page and not have to edit it
818
819  Revision 1.67  2004/05/27 17:49:06  rurban
820  renamed DB_Session to DbSession (in CVS also)
821  added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
822  remove leading slash in error message
823  added force_unlock parameter to File_Passwd (no return on stale locks)
824  fixed adodb session AffectedRows
825  added FileFinder helpers to unify local filenames and DATA_PATH names
826  editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
827
828  Revision 1.66  2004/04/29 23:25:12  rurban
829  re-ordered locale init (as in 1.3.9)
830  fixed loadfile with subpages, and merge/restore anyway
831    (sf.net bug #844188)
832
833  Revision 1.65  2004/04/18 01:11:52  rurban
834  more numeric pagename fixes.
835  fixed action=upload with merge conflict warnings.
836  charset changed from constant to global (dynamic utf-8 switching)
837
838  Revision 1.64  2004/04/06 19:48:56  rurban
839  temp workaround for action=edit AddComment form
840
841  Revision 1.63  2004/03/24 19:39:02  rurban
842  php5 workaround code (plus some interim debugging code in XmlElement)
843    php5 doesn't work yet with the current XmlElement class constructors,
844    WikiUserNew does work better than php4.
845  rewrote WikiUserNew user upgrading to ease php5 update
846  fixed pref handling in WikiUserNew
847  added Email Notification
848  added simple Email verification
849  removed emailVerify userpref subclass: just a email property
850  changed pref binary storage layout: numarray => hash of non default values
851  print optimize message only if really done.
852  forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string.
853    prefs should be stored in db or homepage, besides the current session.
854
855  Revision 1.62  2004/03/17 18:41:05  rurban
856  initial_content and template support for CreatePage
857
858  Revision 1.61  2004/03/12 20:59:17  rurban
859  important cookie fix by Konstantin Zadorozhny
860  new editpage feature: JS_SEARCHREPLACE
861
862  Revision 1.60  2004/02/15 21:34:37  rurban
863  PageList enhanced and improved.
864  fixed new WikiAdmin... plugins
865  editpage, Theme with exp. htmlarea framework
866    (htmlarea yet committed, this is really questionable)
867  WikiUser... code with better session handling for prefs
868  enhanced UserPreferences (again)
869  RecentChanges for show_deleted: how should pages be deleted then?
870
871  Revision 1.59  2003/12/07 20:35:26  carstenklapp
872  Bugfix: Concurrent updates broken since after 1.3.4 release: Fatal
873  error: Call to undefined function: gettransformedcontent() in
874  /home/groups/p/ph/phpwiki/htdocs/phpwiki2/lib/editpage.php on line
875  205.
876
877  Revision 1.58  2003/03/10 18:25:22  dairiki
878  Bug/typo fix.  If you use the edit page to un/lock a page, it
879  failed with: Fatal error: Call to a member function on a
880  non-object in editpage.php on line 136
881
882  Revision 1.57  2003/02/26 03:40:22  dairiki
883  New action=create.  Essentially the same as action=edit, except that if the
884  page already exists, it falls back to action=browse.
885
886  This is for use in the "question mark" links for unknown wiki words
887  to avoid problems and confusion when following links from stale pages.
888  (If the "unknown page" has been created in the interim, the user probably
889  wants to view the page before editing it.)
890
891  Revision 1.56  2003/02/21 18:07:14  dairiki
892  Minor, nitpicky, currently inconsequential changes.
893
894  Revision 1.55  2003/02/21 04:10:58  dairiki
895  Fixes for new cached markup.
896  Some minor code cleanups.
897
898  Revision 1.54  2003/02/16 19:47:16  dairiki
899  Update WikiDB timestamp when editing or deleting pages.
900
901  Revision 1.53  2003/02/15 23:20:27  dairiki
902  Redirect back to browse current version of page upon save,
903  even when no changes were made.
904
905  Revision 1.52  2003/01/03 22:22:00  carstenklapp
906  Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
907
908  Revision 1.51  2003/01/03 02:43:26  carstenklapp
909  New class LoadFileConflictPageEditor, for merging / comparing a loaded
910  pgsrc file with an existing page.
911
912  */
913
914 // Local Variables:
915 // mode: php
916 // tab-width: 8
917 // c-basic-offset: 4
918 // c-hanging-comment-ender-p: nil
919 // indent-tabs-mode: nil
920 // End:
921 ?>