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