]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
add new feature ENABLE_PAGE_PUBLIC
[SourceForge/phpwiki.git] / lib / editpage.php
1 <?php
2 rcs_id('$Id$');
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         $changed = false;
237         if (!ENABLE_PAGE_PUBLIC) {
238             if ((bool)$this->page->get('locked') == (bool)$this->locked)
239                 return false;       // Not changed.
240         }
241
242         if (!$this->user->isAdmin()) {
243             // FIXME: some sort of message
244             return false;         // not allowed.
245         }
246
247         if (ENABLE_PAGE_PUBLIC) {
248             if ((bool)$this->page->get('public') != (bool)$this->public) {
249                 $this->page->set('public', (bool)$this->public);
250                 $this->tokens['LOCK_CHANGED_MSG']
251                     = ($this->public 
252                        ? _("Page now public.")
253                        : _("Page now not-public.")) . " ";
254                 $changed = true;
255             }
256
257             $this->page->set('locked', (bool)$this->locked);
258             $this->tokens['LOCK_CHANGED_MSG']
259                 .= $this->locked 
260                 ? _("Page now locked.") 
261                 : _("Page now unlocked.");
262             $changed = true;
263         }
264         return $changed;            // lock changed.
265     }
266
267     function savePage () {
268         $request = &$this->request;
269
270         if ($this->isUnchanged()) {
271             // Allow admin lock/unlock even if
272             // no text changes were made.
273             if ($this->updateLock()) {
274                 $dbi = $request->getDbh();
275                 $dbi->touch();
276             }
277             // Save failed. No changes made.
278             $this->_redirectToBrowsePage();
279             // user will probably not see the rest of this...
280             require_once('lib/display.php');
281             // force browse of current version:
282             $request->setArg('action', false);
283             $request->setArg('version', false);
284             displayPage($request, 'nochanges');
285             return true;
286         }
287
288         if (!$this->user->isAdmin() and $this->isSpam()) {
289             $this->_isSpam = true;
290             return false;
291             /*
292             // Save failed. No changes made.
293             $this->_redirectToBrowsePage();
294             // user will probably not see the rest of this...
295             require_once('lib/display.php');
296             // force browse of current version:
297             $request->setArg('version', false);
298             displayPage($request, 'nochanges');
299             return true;
300             */
301         }
302
303         $page = &$this->page;
304
305         // Include any meta-data from original page version which
306         // has not been explicitly updated.
307         // (Except don't propagate pgsrc_version --- moot for now,
308         //  because at present it never gets into the db...)
309         $meta = $this->selected->getMetaData();
310         unset($meta['pgsrc_version']);
311         $meta = array_merge($meta, $this->meta);
312         
313         // Save new revision
314         $this->_content = $this->getContent();
315         $newrevision = $page->save($this->_content, 
316                                    $this->version == -1 
317                                      ? -1 
318                                      : $this->_currentVersion + 1, 
319                                    // force new?
320                                    $meta);
321         if (!isa($newrevision, 'WikiDB_PageRevision')) {
322             // Save failed.  (Concurrent updates).
323             return false;
324         }
325         
326         // New contents successfully saved...
327         $this->updateLock();
328
329         // Clean out archived versions of this page.
330         require_once('lib/ArchiveCleaner.php');
331         $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
332         $cleaner->cleanPageRevisions($page);
333
334         /* generate notification emails done in WikiDB::save to catch 
335          all direct calls (admin plugins) */
336
337         // look at the errorstack
338         $errors   = $GLOBALS['ErrorManager']->_postponed_errors;
339         $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML(); 
340         $GLOBALS['ErrorManager']->_postponed_errors = $errors;
341
342         $dbi = $request->getDbh();
343         $dbi->touch();
344         
345         global $WikiTheme;
346         if (empty($warnings->_content) && ! $WikiTheme->getImageURL('signature')) {
347             // Do redirect to browse page if no signature has
348             // been defined.  In this case, the user will most
349             // likely not see the rest of the HTML we generate
350             // (below).
351             $request->setArg('action', false);
352             $this->_redirectToBrowsePage();
353         }
354
355         // Force browse of current page version.
356         $request->setArg('version', false);
357         // testme: does preview and more need action=edit?
358         $request->setArg('action', false);
359
360         $template = Template('savepage', $this->tokens);
361         $template->replace('CONTENT', $newrevision->getTransformedContent());
362         if (!empty($warnings->_content)) {
363             $template->replace('WARNINGS', $warnings);
364             unset($GLOBALS['ErrorManager']->_postponed_errors); 
365         }
366
367         $pagelink = WikiLink($page);
368
369         GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
370         return true;
371     }
372
373     function isConcurrentUpdate () {
374         assert($this->current->getVersion() >= $this->_currentVersion);
375         return $this->current->getVersion() != $this->_currentVersion;
376     }
377
378     function canEdit () {
379         return !$this->page->get('locked') || $this->user->isAdmin();
380     }
381
382     function isInitialEdit () {
383         return $this->_initialEdit;
384     }
385
386     function isUnchanged () {
387         $current = &$this->current;
388
389         if ($this->meta['markup'] !=  $current->get('markup'))
390             return false;
391
392         return $this->_content == $current->getPackedContent();
393     }
394
395     /** 
396      * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
397      * Need to check dynamically some blacklist wikipage settings 
398      * (plugin WikiAccessRestrictions) and some static blacklist.
399      * DONE: 
400      *   Always: More then 20 new external links
401      *   ENABLE_SPAMASSASSIN:  content patterns by babycart (only php >= 4.3 for now)
402      *   ENABLE_SPAMBLOCKLIST: content domain blacklist
403      */
404     function isSpam () {
405         $current = &$this->current;
406         $request = &$this->request;
407
408         $oldtext = $current->getPackedContent();
409         $newtext =& $this->_content;
410         $numlinks = $this->numLinks($newtext);
411         $newlinks = $numlinks - $this->numLinks($oldtext);
412         // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
413         //        better use a certain text : link ratio.
414
415         // 1. Not more then 20 new external links
416         if ($newlinks >= NUM_SPAM_LINKS)
417         {
418             // Allow strictly authenticated users?
419             // TODO: mail the admin?
420             $this->tokens['PAGE_LOCKED_MESSAGE'] = 
421                 HTML($this->getSpamMessage(),
422                      HTML::p(HTML::strong(_("Too many external links."))));
423             return true;
424         }
425         // 2. external babycart (SpamAssassin) check
426         // This will probably prevent from discussing sex or viagra related topics. So beware.
427         if (ENABLE_SPAMASSASSIN) {
428             require_once("lib/spam_babycart.php");
429             if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"), 
430                                            $this->user->getId())) {
431                 // TODO: mail the admin
432                 if (is_array($babycart))
433                     $this->tokens['PAGE_LOCKED_MESSAGE'] = 
434                         HTML($this->getSpamMessage(),
435                              HTML::p(HTML::em(_("SpamAssassin reports: "), 
436                                                 join("\n", $babycart))));
437                 return true;
438             }
439         }
440         // 3. extract (new) links and check surbl for blocked domains
441         if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
442             require_once("lib/SpamBlocklist.php");
443             require_once("lib/InlineParser.php");
444             $parsed = TransformLinks($newtext);
445             $oldparsed = TransformLinks($oldtext);
446             $oldlinks = array();
447             foreach ($oldparsed->_content as $link) {
448                 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
449                     $uri = $link->_getURL($this->page->getName());
450                     $oldlinks[$uri]++;
451                 }
452             }
453             unset($oldparsed);
454             foreach ($parsed->_content as $link) {
455                 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
456                     $uri = $link->_getURL($this->page->getName());
457                     // only check new links, so admins may add blocked links.
458                     if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
459                         // TODO: mail the admin
460                         $this->tokens['PAGE_LOCKED_MESSAGE'] = 
461                             HTML($this->getSpamMessage(),
462                                  HTML::p(HTML::strong(_("External links contain blocked domains:")),
463                                          HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"), 
464                                                                    $uri." [".$res[2]."]", $res[0], $res[1])))));
465                         return true;
466                     }
467                 }
468             }
469             unset($oldlinks);
470             unset($parsed);
471             unset($oldparsed);
472         }
473
474         return false;
475     }
476
477     /** Number of external links in the wikitext
478      */
479     function numLinks(&$text) {
480         return substr_count($text, "http://") + substr_count($text, "https://");
481     }
482
483     /** Header of the Anti Spam message 
484      */
485     function getSpamMessage () {
486         return
487             HTML(HTML::h2(_("Spam Prevention")),
488                  HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
489                          HTML::br(),
490                          _("Sorry for the inconvenience.")),
491                  HTML::p(""));
492     }
493
494     function getPreview () {
495         require_once('lib/PageType.php');
496         $this->_content = $this->getContent();
497         return new TransformedText($this->page, $this->_content, $this->meta);
498     }
499
500     function getConvertedPreview () {
501         require_once('lib/PageType.php');
502         $this->_content = $this->getContent();
503         $this->meta['markup'] = 2.0;
504         $this->_content = ConvertOldMarkup($this->_content);
505         return new TransformedText($this->page, $this->_content, $this->meta);
506     }
507
508     function getDiff () {
509         require_once('lib/diff.php');
510         $html = HTML();
511         
512         $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
513         if ($diff->isEmpty()) {
514             $html->pushContent(HTML::hr(),
515                                HTML::p('[', _("Versions are identical"),
516                                        ']'));
517         }
518         else {
519             // New CSS formatted unified diffs (ugly in NS4).
520             $fmt = new HtmlUnifiedDiffFormatter;
521             // Use this for old table-formatted diffs.
522             //$fmt = new TableUnifiedDiffFormatter;
523             $html->pushContent($fmt->format($diff));
524         }
525         return $html;
526     }
527
528     // possibly convert HTMLAREA content back to Wiki markup
529     function getContent () {
530         if (ENABLE_WYSIWYG) {
531             // don't store everything as html
532             if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
533                 // Wikiwyg shortcut to avoid the InlineTransformer:
534                 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
535                 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
536                 $this->_content = join("", $xml_output->_content);
537             } else {
538                 $this->meta['pagetype'] = 'html';
539             }
540             return $this->_content;
541         } else {
542             return $this->_content;
543         }
544     }
545
546     function getLockedMessage () {
547         return
548             HTML(HTML::h2(_("Page Locked")),
549                  HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
550                  HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
551                  HTML::p(_("Sorry for the inconvenience.")));
552     }
553
554     function isModerated() {
555         return $this->page->get('moderation');
556     }
557     function getModeratedMessage() {
558         return
559             HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
560                  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")))),
561                  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.", 
562                          WikiLink(_("UserPreferences")))));
563     }
564     function getConflictMessage ($unresolved = false) {
565         /*
566          xgettext only knows about c/c++ line-continuation strings
567          it does not know about php's dot operator.
568          We want to translate this entire paragraph as one string, of course.
569          */
570
571         //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
572
573         if ($unresolved)
574             $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.",
575                                 "<<<<<<< ". _("Your version"),
576                                 ">>>>>>> ". _("Other version")));
577         else
578             $message = HTML::p(_("Please check it through before saving."));
579
580
581
582         /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
583           HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
584                        $re_edit_link)),
585           HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
586           HTML::li(_("Save your updated changes.")));
587         */
588         return
589             HTML(HTML::h2(_("Conflicting Edits!")),
590                  HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
591                  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.")),
592                  $message);
593     }
594
595
596     function getTextArea () {
597         $request = &$this->request;
598
599         $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
600
601         // WYSIWYG will need two pagetypes: raw wikitest and converted html
602         if (ENABLE_WYSIWYG) {
603             $this->_wikicontent = $this->_content;
604             $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
605             //                $this->getPreview();
606             //$this->_htmlcontent = $this->_content->asXML();
607         }
608
609         $textarea = HTML::textarea(array('class'=> 'wikiedit',
610                                          'name' => 'edit[content]',
611                                          'id'   => 'edit-content',
612                                          'rows' => $request->getPref('editHeight'),
613                                          'cols' => $request->getPref('editWidth'),
614                                          'readonly' => (bool) $readonly),
615                                    $this->_content);
616         /** <textarea wrap="virtual"> is not valid XHTML but Netscape 4 requires it
617          * to wrap long lines.
618          */
619         if (isBrowserNS4())
620             $textarea->setAttr('wrap', 'virtual');
621         if (ENABLE_WYSIWYG) {
622             return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent, 
623                                                 $textarea->getAttr('name'));
624         } else
625             return $textarea;
626     }
627
628     function getFormElements () {
629         global $WikiTheme;
630         $request = &$this->request;
631         $page = &$this->page;
632
633         $h = array('action'   => 'edit',
634                    'pagename' => $page->getName(),
635                    'version'  => $this->version,
636                    'edit[pagetype]' => $this->meta['pagetype'],
637                    'edit[current_version]' => $this->_currentVersion);
638
639         $el['HIDDEN_INPUTS'] = HiddenInputs($h);
640         $el['EDIT_TEXTAREA'] = $this->getTextArea();
641         if ( ENABLE_CAPTCHA ) {
642             $el = array_merge($el, $this->Captcha->getFormElements());
643         }
644         $el['SUMMARY_INPUT']
645             = HTML::input(array('type'  => 'text',
646                                 'class' => 'wikitext',
647                                 'id' => 'edit-summary',
648                                 'name'  => 'edit[summary]',
649                                 'size'  => 50,
650                                 'maxlength' => 256,
651                                 'value' => $this->meta['summary']));
652         $el['MINOR_EDIT_CB']
653             = HTML::input(array('type' => 'checkbox',
654                                 'name'  => 'edit[minor_edit]',
655                                 'id' => 'edit-minor_edit',
656                                 'checked' => (bool) $this->meta['is_minor_edit']));
657         $el['OLD_MARKUP_CB']
658             = HTML::input(array('type' => 'checkbox',
659                                 'name' => 'edit[markup]',
660                                 'value' => 'old',
661                                 'checked' => $this->meta['markup'] < 2.0,
662                                 'id' => 'useOldMarkup',
663                                 'onclick' => 'showOldMarkupRules(this.checked)'));
664         $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0) 
665             ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
666         $el['LOCKED_CB']
667             = HTML::input(array('type' => 'checkbox',
668                                 'name' => 'edit[locked]',
669                                 'id'   => 'edit-locked',
670                                 'disabled' => (bool) !$this->user->isadmin(),
671                                 'checked'  => (bool) $this->locked));
672
673         if (ENABLE_PAGE_PUBLIC) {
674             $el['PUBLIC_CB']
675             = HTML::input(array('type' => 'checkbox',
676                                 'name' => 'edit[public]',
677                                 'id'   => 'edit-public',
678                                 'disabled' => (bool) !$this->user->isAdmin(),
679                                 'checked'  => (bool) $this->page->get('public')));
680         }
681         if (ENABLE_WYSIWYG) {
682             if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
683                 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
684             }
685         }
686
687         $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
688                                   'wikiaction',
689                                   array('accesskey'=> 'p', 
690                                      'title' => 'Preview the current content [alt-p]'));
691
692         //if (!$this->isConcurrentUpdate() && $this->canEdit())
693         $el['SAVE_B'] = Button('submit:edit[save]',
694                                _("Save"), 'wikiaction',
695                                array('accesskey'=> 's', 
696                                      'title' => 'Save the current content as wikipage [alt-s]'));
697         $el['CHANGES_B'] = Button('submit:edit[diff]',
698                                _("Changes"), 'wikiaction',
699                                array('accesskey'=> 'c', 
700                                      'title' => 'Preview the current changes as diff [alt-c]'));
701         $el['UPLOAD_B'] = Button('submit:edit[upload]',
702                                _("Upload"), 'wikiaction',
703                                 array('title' => 'Select a local file and press Upload to attach into this page'));
704         $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
705                                _("Spell Check"), 'wikiaction',
706                                 array('title' => 'Check the spelling'));
707         $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
708
709         $el['WIDTH_PREF'] 
710             = HTML::input(array('type'     => 'text',
711                                 'size'     => 3,
712                                 'maxlength'=> 4,
713                                 'class'    => "numeric",
714                                 'name'     => 'pref[editWidth]',
715                                 'id'       => 'pref-editWidth',
716                                 'value'    => $request->getPref('editWidth'),
717                                 'onchange' => 'this.form.submit();'));
718         $el['HEIGHT_PREF'] 
719             = HTML::input(array('type'     => 'text',
720                                 'size'     => 3,
721                                 'maxlength'=> 4,
722                                 'class'    => "numeric",
723                                 'name'     => 'pref[editHeight]',
724                                 'id'       => 'pref-editHeight',
725                                 'value'    => $request->getPref('editHeight'),
726                                 'onchange' => 'this.form.submit();'));
727         $el['SEP'] = $WikiTheme->getButtonSeparator();
728         $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.", 
729                                     HTML::em($this->user->getId()));
730         
731         return $el;
732     }
733
734     function _redirectToBrowsePage() {
735         $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
736     }
737
738     function _restoreState () {
739         $request = &$this->request;
740
741         $posted = $request->getArg('edit');
742         $request->setArg('edit', false);
743
744         if (!$posted 
745             || !$request->isPost()
746             || !in_array($request->getArg('action'),array('edit','loadfile')))
747             return false;
748
749         if (!isset($posted['content']) || !is_string($posted['content']))
750             return false;
751         $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
752                                         rtrim($posted['content']));
753         $this->_content = $this->getContent();
754
755         $this->_currentVersion = (int) $posted['current_version'];
756
757         if ($this->_currentVersion < 0)
758             return false;
759         if ($this->_currentVersion > $this->current->getVersion())
760             return false;       // FIXME: some kind of warning?
761
762         $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
763         $meta['markup'] = $is_old_markup ? false : 2.0;
764         $meta['summary'] = trim(substr($posted['summary'], 0, 256));
765         $meta['is_minor_edit'] = !empty($posted['minor_edit']);
766         $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
767         if ( ENABLE_CAPTCHA )
768             $meta['captcha_input'] = !empty($posted['captcha_input']) ?
769                 $posted['captcha_input'] : '';
770
771         $this->meta = array_merge($this->meta, $meta);
772         $this->locked = !empty($posted['locked']);
773         if (ENABLE_PAGE_PUBLIC)
774             $this->public = !empty($posted['public']);
775
776         foreach (array('preview','save','edit_convert',
777                        'keep_old','overwrite','diff','upload') as $o) 
778         {
779             if (!empty($posted[$o]))
780                 $this->editaction = $o;
781         }
782         if (empty($this->editaction))
783             $this->editaction = 'edit';
784
785         return true;
786     }
787
788     function _initializeState () {
789         $request = &$this->request;
790         $current = &$this->current;
791         $selected = &$this->selected;
792         $user = &$this->user;
793
794         if (!$selected)
795             NoSuchRevision($request, $this->page, $this->version); // noreturn
796
797         $this->_currentVersion = $current->getVersion();
798         $this->_content = $selected->getPackedContent();
799
800         $this->locked = $this->page->get('locked');
801
802         // If author same as previous author, default minor_edit to on.
803         $age = $this->meta['mtime'] - $current->get('mtime');
804         $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
805                                          && $current->get('author') == $user->getId()
806                                          );
807
808         // Default for new pages is new-style markup.
809         if ($selected->hasDefaultContents())
810             $is_new_markup = true;
811         else
812             $is_new_markup = $selected->get('markup') >= 2.0;
813
814         $this->meta['markup'] = $is_new_markup ? 2.0: false;
815         $this->meta['pagetype'] = $selected->get('pagetype');
816         if ($this->meta['pagetype'] == 'wikiblog')
817             $this->meta['summary'] = $selected->get('summary'); // keep blog title
818         else
819             $this->meta['summary'] = '';
820         $this->editaction = 'edit';
821     }
822 }
823
824 class LoadFileConflictPageEditor
825 extends PageEditor
826 {
827     function editPage ($saveFailed = true) {
828         $tokens = &$this->tokens;
829
830         if (!$this->canEdit()) {
831             if ($this->isInitialEdit()) {
832                 return $this->viewSource();
833             }
834             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
835         }
836         elseif ($this->editaction == 'save') {
837             if ($this->savePage()) {
838                 return true;    // Page saved.
839             }
840             $saveFailed = true;
841         }
842
843         if ($saveFailed || $this->isConcurrentUpdate())
844         {
845             // Get the text of the original page, and the two conflicting edits
846             // The diff class takes arrays as input.  So retrieve content as
847             // an array, or convert it as necesary.
848             $orig = $this->page->getRevision($this->_currentVersion);
849             $this_content = explode("\n", $this->_content);
850             $other_content = $this->current->getContent();
851             require_once("lib/diff.php");
852             $diff2 = new Diff($other_content, $this_content);
853             $context_lines = max(4, count($other_content) + 1,
854                                  count($this_content) + 1);
855             $fmt = new BlockDiffFormatter($context_lines);
856
857             $this->_content = $fmt->format($diff2);
858             // FIXME: integrate this into class BlockDiffFormatter
859             $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
860                                           $this->_content);
861             $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
862                                           $this->_content);
863
864             $this->_currentVersion = $this->current->getVersion();
865             $this->version = $this->_currentVersion;
866             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
867         }
868
869         if ($this->editaction == 'edit_convert')
870             $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
871         if ($this->editaction == 'preview')
872             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
873
874         // FIXME: NOT_CURRENT_MESSAGE?
875         $tokens = array_merge($tokens, $this->getFormElements());
876         // we need all GET params for loadfile overwrite
877         if ($this->request->getArg('action') == 'loadfile') {
878                 
879             $this->tokens['HIDDEN_INPUTS'] = 
880                 HTML(HiddenInputs
881                     (array('source' => $this->request->getArg('source'),
882                            'merge'  => 1)),
883                      $this->tokens['HIDDEN_INPUTS']);
884             // add two conflict resolution buttons before preview and save.
885             $tokens['PREVIEW_B'] = HTML(
886                                     Button('submit:edit[keep_old]', 
887                                            _("Keep old"), 'wikiaction'),
888                                     $tokens['SEP'],
889                                     Button('submit:edit[overwrite]', 
890                                            _("Overwrite with new"), 'wikiaction'),
891                                     $tokens['SEP'],
892                                     $tokens['PREVIEW_B']);
893         }
894         if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
895             include_once("lib/EditToolbar.php");
896             $toolbar = new EditToolbar();
897             $tokens = array_merge($tokens, $toolbar->getTokens());
898         }
899
900         return $this->output('editpage', _("Merge and Edit: %s"));
901     }
902
903     function output ($template, $title_fs) {
904         $selected = &$this->selected;
905         $current = &$this->current;
906
907         if ($selected && $selected->getVersion() != $current->getVersion()) {
908             $rev = $selected;
909             $pagelink = WikiLink($selected);
910         }
911         else {
912             $rev = $current;
913             $pagelink = WikiLink($this->page);
914         }
915
916         $title = new FormattedText ($title_fs, $pagelink);
917         $this->tokens['HEADER'] = $title;
918         //hack! there's no TITLE in editpage, but in the previous top template
919         if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
920             $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
921         else
922             $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
923                                                         $this->tokens['PAGE_LOCKED_MESSAGE']);
924         $template = Template($template, $this->tokens);
925
926         //GeneratePage($template, $title, $rev);
927         PrintXML($template);
928         return true;
929     }
930
931     function getConflictMessage () {
932         $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.",
933                                     "<<<<<<<",
934                                     "======="),
935                                 HTML::p(_("Please check it through before saving."))));
936         return $message;
937     }
938 }
939
940 // Local Variables:
941 // mode: php
942 // tab-width: 8
943 // c-basic-offset: 4
944 // c-hanging-comment-ender-p: nil
945 // indent-tabs-mode: nil
946 // End:
947 ?>