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