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