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