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