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