2 rcs_id('$Id: editpage.php,v 1.85 2004-12-06 19:49:56 rurban Exp $');
4 require_once('lib/Template.php');
6 // USE_HTMLAREA - Support for some WYSIWYG HTML Editor
7 // Not yet enabled, since we cannot convert HTML to Wiki Markup yet.
8 // (See HtmlParser.php for the ongoing efforts)
9 // We might use a HTML PageType, which is contra wiki, but some people might prefer HTML markup.
10 // TODO: Change from constant to user preference variable (checkbox setting),
11 // when HtmlParser is finished.
12 if (!defined('USE_HTMLAREA')) define('USE_HTMLAREA', false);
13 if (USE_HTMLAREA) require_once('lib/htmlarea.php');
17 function PageEditor (&$request) {
18 $this->request = &$request;
20 $this->user = $request->getUser();
21 $this->page = $request->getPage();
23 $this->current = $this->page->getCurrentRevision(false);
25 // HACKish short circuit to browse on action=create
26 if ($request->getArg('action') == 'create') {
27 if (! $this->current->hasDefaultContents())
28 $request->redirect(WikiURL($this->page->getName())); // noreturn
32 $this->meta = array('author' => $this->user->getId(),
33 'author_id' => $this->user->getAuthenticatedId(),
36 $this->tokens = array();
38 $version = $request->getArg('version');
39 if ($version !== false) {
40 $this->selected = $this->page->getRevision($version);
41 $this->version = $version;
44 $this->version = $this->current->getVersion();
45 $this->selected = $this->page->getRevision($this->version);
48 if ($this->_restoreState()) {
49 $this->_initialEdit = false;
52 $this->_initializeState();
53 $this->_initialEdit = true;
55 // The edit request has specified some initial content from a template
56 if ( ($template = $request->getArg('template')) and
57 $request->_dbi->isWikiPage($template)) {
58 $page = $request->_dbi->getPage($template);
59 $current = $page->getCurrentRevision();
60 $this->_content = $current->getPackedContent();
61 } elseif ($initial_content = $request->getArg('initial_content')) {
62 $this->_content = $initial_content;
63 $this->_redirect_to = $request->getArg('save_and_redirect_to');
67 header("Content-Type: text/html; charset=" . $GLOBALS['charset']);
70 function editPage () {
73 $tokens = &$this->tokens;
75 if (! $this->canEdit()) {
76 if ($this->isInitialEdit())
77 return $this->viewSource();
78 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
80 elseif ($this->request->getArg('save_and_redirect_to') != "") {
81 if ($this->savePage()) {
83 $this->request->redirect(WikiURL($this->request->getArg('save_and_redirect_to')));
84 return true; // Page saved.
88 elseif ($this->editaction == 'save') {
89 if ($this->savePage()) {
90 return true; // Page saved.
95 if ($saveFailed and $this->isConcurrentUpdate())
97 // Get the text of the original page, and the two conflicting edits
98 // The diff3 class takes arrays as input. So retrieve content as
99 // an array, or convert it as necesary.
100 $orig = $this->page->getRevision($this->_currentVersion);
101 // FIXME: what if _currentVersion has be deleted?
102 $orig_content = $orig->getContent();
103 $this_content = explode("\n", $this->_content);
104 $other_content = $this->current->getContent();
105 include_once("lib/diff3.php");
106 $diff = new diff3($orig_content, $this_content, $other_content);
107 $output = $diff->merged_output(_("Your version"), _("Other version"));
108 // Set the content of the textarea to the merged diff
109 // output, and update the version
110 $this->_content = implode ("\n", $output);
111 $this->_currentVersion = $this->current->getVersion();
112 $this->version = $this->_currentVersion;
113 $unresolved = $diff->ConflictingBlocks;
114 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage($unresolved);
115 } elseif ($saveFailed) {
116 $tokens['CONCURRENT_UPDATE_MESSAGE'] =
117 HTML(HTML::h2(_("Some internal editing error")),
118 HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
119 HTML::p(HTML::em(_("&version=-1 might help."))));
122 if ($this->editaction == 'preview')
123 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
125 // FIXME: NOT_CURRENT_MESSAGE?
126 $tokens = array_merge($tokens, $this->getFormElements());
128 //FIXME: enable Undo button for all other buttons also, not only the search/replace button
129 if (defined('JS_SEARCHREPLACE') and JS_SEARCHREPLACE) {
130 $tokens['JS_SEARCHREPLACE'] = 1;
131 $undo_btn = $WikiTheme->getImageURL("ed_undo.png");
132 $undo_d_btn = $WikiTheme->getImageURL("ed_undo_d.png");
133 // JS_SEARCHREPLACE from walterzorn.de
134 $WikiTheme->addMoreHeaders(Javascript("
135 var f, sr_undo, replacewin, undo_buffer=new Array(), undo_buffer_index=0;
137 function define_f() {
138 f=document.getElementById('editpage');
139 f.editarea=document.getElementById('edit[content]');
140 sr_undo=document.getElementById('sr_undo');
144 function undo_enable(bool) {
146 sr_undo.src='".$undo_btn."';
150 sr_undo.disabled = false;
152 sr_undo.src='".$undo_d_btn."';
156 sr_undo.disabled = true;
157 if(sr_undo.blur) sr_undo.blur();
161 replacewin = window.open('','','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,height=90,width=450');
162 replacewin.window.document.write('<html><head><title>"
163 ._("Search & Replace")
164 ."</title><style type=\"text/css\"><'+'!'+'-- body, input {font-family:Tahoma,Arial,Helvetica,sans-serif;font-size:10pt;font-weight:bold;} td {font-size:9pt} --'+'></style></head><body bgcolor=\"#dddddd\" onload=\"if(document.forms[0].ein.focus) document.forms[0].ein.focus(); return false;\"><form><center><table><tr><td align=\"right\">'+'"
166 .":</td><td align=\"left\"><input type=\"text\" name=\"ein\" size=\"45\" maxlength=\"500\"></td></tr><tr><td align=\"right\">'+' "
168 .":</td><td align=\"left\"><input type=\"text\" name=\"aus\" size=\"45\" maxlength=\"500\"></td></tr><tr><td colspan=\"2\" align=\"center\"><input type=\"button\" value=\" "
170 ." \" onclick=\"if(self.opener)self.opener.do_replace(); return false;\"> <input type=\"button\" value=\""
172 ."\" onclick=\"self.close(); return false;\"></td></tr></table></center></form></body></html>');
173 replacewin.window.document.close();
176 function do_replace() {
177 var txt=undo_buffer[undo_buffer_index]=f.editarea.value, ein=new RegExp(replacewin.document.forms[0].ein.value,'g'), aus=replacewin.document.forms[0].aus.value;
178 if (ein==''||ein==null) {
179 if (replacewin) replacewin.window.document.forms[0].ein.focus();
182 var z_repl=txt.match(ein)? txt.match(ein).length : 0;
183 txt=txt.replace(ein,aus);
184 ein=ein.toString().substring(1,ein.toString().length-2);
185 result(z_repl, 'Substring \"'+ein+'\" found '+z_repl+' times. Replace with \"'+aus+'\"?', txt, 'String \"'+ein+'\" not found.');
186 replacewin.window.focus();
187 replacewin.window.document.forms[0].ein.focus();
190 function result(zahl,frage,txt,alert_txt) {
192 if(window.confirm(frage)==true) {
193 f.editarea.value=txt;
197 } else alert(alert_txt);
200 if(undo_buffer_index==0) return;
201 else if(undo_buffer_index>0) {
202 f.editarea.value=undo_buffer[undo_buffer_index-1];
203 undo_buffer[undo_buffer_index]=null;
205 if(undo_buffer_index==0) {
207 _("Operation undone")
213 //save a snapshot in the undo buffer (unused)
214 function undo_save() {
215 undo_buffer[undo_buffer_index]=f.editarea.value;
220 $WikiTheme->addMoreAttr('body'," onload='define_f()'");
222 $WikiTheme->addMoreAttr('body',"document.getElementById('edit[content]').editarea.focus()");
224 if (defined('ENABLE_EDIT_TOOLBAR') and ENABLE_EDIT_TOOLBAR) {
225 $WikiTheme->addMoreHeaders(JavaScript('',array('src' => $WikiTheme->_findData("toolbar.js"))));
226 $tokens['EDIT_TOOLBAR'] = $this->toolbar();
228 $tokens['EDIT_TOOLBAR'] = '';
231 return $this->output('editpage', _("Edit: %s"));
234 function toolbar () {
238 "image"=>"ed_format_bold.png",
241 "sample"=>_("Bold text"),
242 "tip"=>_("Bold text")),
243 array("image"=>"ed_format_italic.png",
246 "sample"=>_("Italic text"),
247 "tip"=>_("Italic text")),
248 array("image"=>"ed_pagelink.png",
251 "sample"=>_("optional label | PageName"),
252 "tip"=>_("Link to page")),
253 array("image"=>"ed_link.png",
256 "sample"=>_("optional label | http://www.example.com"),
257 "tip"=>_("External link (remember http:// prefix)")),
258 array("image"=>"ed_headline.png",
261 "sample"=>_("Headline text"),
262 "tip"=>_("Level 1 headline")),
263 array("image"=>"ed_image.png",
266 "sample"=>_("Example.jpg"),
267 "tip"=>_("Embedded image")),
268 array("image"=>"ed_nowiki.png",
269 "open"=>"\\n\\<verbatim\\>\\n",
270 "close"=>"\\n\\</verbatim\\>\\n",
271 "sample"=>_("Insert non-formatted text here"),
272 "tip"=>_("Ignore wiki formatting")),
273 array("image"=>"ed_sig.png",
274 "open" => " --" . $GLOBALS['request']->_user->UserName(),
277 "tip"=>_("Your signature")),
278 array("image"=>"ed_hr.png",
279 "open"=>"\\n----\\n",
282 "tip"=>_("Horizontal line"))
284 $toolbar = "document.writeln(\"<div class=\\\"edit-toolbar\\\" id=\\\"toolbar\\\">\");\n";
286 $btn = new SubmitImageButton(_("Save"), "edit[save]", 'toolbar', $WikiTheme->getImageURL("ed_save.png"));
287 $btn->addTooltip(_("Save"));
288 $toolbar.='document.writeln("'.addslashes($btn->asXml()).'");'."\n";
289 $btn = new SubmitImageButton(_("Preview"), "edit[preview]", 'toolbar', $WikiTheme->getImageURL("ed_preview.png"));
290 $btn->addTooltip(_("Preview"));
291 $toolbar.='document.writeln("'.addslashes($btn->asXml()).'");'."\n";
293 foreach ($toolarray as $tool) {
294 $image = $WikiTheme->getImageURL($tool["image"]);
295 $open = $tool["open"];
296 $close = $tool["close"];
297 $sample = addslashes( $tool["sample"] );
298 // Note that we use the tip both for the ALT tag and the TITLE tag of the image.
299 // Older browsers show a "speedtip" type message only for ALT.
300 // Ideally these should be different, realistically they
301 // probably don't need to be.
302 $tip = addslashes( $tool["tip"] );
303 $toolbar.="addTagButton('$image','$tip','$open','$close','$sample');\n";
305 $toolbar.="addInfobox('" . addslashes( _("Click a button to get an example text") ) . "');\n";
307 if (JS_SEARCHREPLACE) {
308 $undo_d_btn = $WikiTheme->getImageURL("ed_undo_d.png");
309 //$redo_btn = $WikiTheme->getImageURL("ed_redo.png");
310 $sr_btn = $WikiTheme->getImageURL("ed_replace.png");
311 $sr_html = HTML(HTML::img(array('class'=>"toolbar",
314 'title'=>_("Undo Search & Replace"),
315 'alt' =>_("Undo Search & Replace"),
316 'disabled'=>"disabled",
318 'onfocus' =>"if(this.blur && undo_buffer_index==0) this.blur()",
319 'onclick' =>"do_undo()")),
320 HTML::img(array('class'=>"toolbar",
322 'alt' =>_("Search & Replace"),
323 'title'=>_("Search & Replace"),
324 'onclick'=>"replace()")));
329 // Button to generate pagenames, display in extra window as pulldown and insert
330 // Button to generate plugins, display in extra window as pulldown and insert
331 // Button to generate categories, display in extra window as pulldown and insert
332 if (DEBUG and JS_SEARCHREPLACE) {
333 //TODO: delegate this calculation to a seperate pulldown action request
334 require_once('lib/TextSearchQuery.php');
335 $dbi =& $GLOBALS['request']->_dbi;
336 $pages = $dbi->titleSearch(new TextSearchQuery(''.
337 _("Category").' OR '.
339 if ($pages->count()) {
340 $categories = array();
341 while ($p = $pages->next()){
342 $categories[] = $p->getName();
344 $more_buttons = HTML::img(array('class'=>"toolbar",
345 'src' => $WikiTheme->getImageURL("ed_category.png"),
346 'title'=>_("AddCategory"),
347 'onclick'=>"showPulldown('".
348 _("Insert Categories (double-click)")
349 ."',['".join("','",$categories)."'],'"
353 if ($sr_html) $sr_html = HTML($sr_html, $more_buttons);
354 else $sr_html = $more_buttons;
357 $toolbar_end = "document.writeln(\"</div>\");";
358 // don't use document.write for replace, otherwise self.opener is not defined.
360 return HTML(Javascript($toolbar),
362 Javascript($toolbar_end));
364 return HTML(Javascript($toolbar . $toolbar_end));
367 function output ($template, $title_fs) {
369 $selected = &$this->selected;
370 $current = &$this->current;
372 if ($selected && $selected->getVersion() != $current->getVersion()) {
374 $pagelink = WikiLink($selected);
378 $pagelink = WikiLink($this->page);
382 $title = new FormattedText ($title_fs, $pagelink);
383 if (USE_HTMLAREA and $template == 'editpage') {
384 $WikiTheme->addMoreHeaders(Edit_HtmlArea_Head());
385 //$tokens['PAGE_SOURCE'] = Edit_HtmlArea_ConvertBefore($this->_content);
387 $template = Template($template, $this->tokens);
388 GeneratePage($template, $title, $rev);
393 function viewSource () {
394 assert($this->isInitialEdit());
395 assert($this->selected);
397 $this->tokens['PAGE_SOURCE'] = $this->_content;
398 return $this->output('viewsource', _("View Source: %s"));
401 function updateLock() {
402 if ((bool)$this->page->get('locked') == (bool)$this->locked)
403 return false; // Not changed.
405 if (!$this->user->isAdmin()) {
406 // FIXME: some sort of message
407 return false; // not allowed.
410 $this->page->set('locked', (bool)$this->locked);
411 $this->tokens['LOCK_CHANGED_MSG']
412 = $this->locked ? _("Page now locked.") : _("Page now unlocked.");
414 return true; // lock changed.
417 function savePage () {
418 $request = &$this->request;
420 if ($this->isUnchanged()) {
421 // Allow admin lock/unlock even if
422 // no text changes were made.
423 if ($this->updateLock()) {
424 $dbi = $request->getDbh();
427 // Save failed. No changes made.
428 $this->_redirectToBrowsePage();
429 // user will probably not see the rest of this...
430 include_once('lib/display.php');
431 // force browse of current version:
432 $request->setArg('version', false);
433 displayPage($request, 'nochanges');
437 if ($this->isSpam()) {
440 // Save failed. No changes made.
441 $this->_redirectToBrowsePage();
442 // user will probably not see the rest of this...
443 include_once('lib/display.php');
444 // force browse of current version:
445 $request->setArg('version', false);
446 displayPage($request, 'nochanges');
451 $page = &$this->page;
453 // Include any meta-data from original page version which
454 // has not been explicitly updated.
455 // (Except don't propagate pgsrc_version --- moot for now,
456 // because at present it never gets into the db...)
457 $meta = $this->selected->getMetaData();
458 unset($meta['pgsrc_version']);
459 $meta = array_merge($meta, $this->meta);
462 $this->_content = $this->getContent();
463 $newrevision = $page->save($this->_content,
464 $this->version == -1 ? -1 : $this->_currentVersion + 1, // force new?
466 if (!isa($newrevision, 'WikiDB_PageRevision')) {
467 // Save failed. (Concurrent updates).
471 // New contents successfully saved...
474 // Clean out archived versions of this page.
475 include_once('lib/ArchiveCleaner.php');
476 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
477 $cleaner->cleanPageRevisions($page);
479 /* generate notification emails done in WikiDB::save to catch all direct calls
482 $dbi = $request->getDbh();
483 $warnings = $dbi->GenericWarnings();
487 if (empty($warnings) && ! $WikiTheme->getImageURL('signature')) {
488 // Do redirect to browse page if no signature has
489 // been defined. In this case, the user will most
490 // likely not see the rest of the HTML we generate
492 $this->_redirectToBrowsePage();
495 // Force browse of current page version.
496 $request->setArg('version', false);
497 //$request->setArg('action', false);
499 $template = Template('savepage', $this->tokens);
500 $template->replace('CONTENT', $newrevision->getTransformedContent());
501 if (!empty($warnings))
502 $template->replace('WARNINGS', $warnings);
504 $pagelink = WikiLink($page);
506 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
510 function isConcurrentUpdate () {
511 assert($this->current->getVersion() >= $this->_currentVersion);
512 return $this->current->getVersion() != $this->_currentVersion;
515 function canEdit () {
516 return !$this->page->get('locked') || $this->user->isAdmin();
519 function isInitialEdit () {
520 return $this->_initialEdit;
523 function isUnchanged () {
524 $current = &$this->current;
526 if ($this->meta['markup'] != $current->get('markup'))
529 return $this->_content == $current->getPackedContent();
533 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
534 * Need to check dynamically some blacklist wikipage settings (plugin WikiAccessRestrictions)
535 * and some static blacklist.
537 * More then 20 new external links
538 * content patterns by babycart (only php >= 4.3 for now)
545 $current = &$this->current;
546 $request = &$this->request;
548 $oldtext = $current->getPackedContent();
549 $newtext =& $this->_content;
550 // 1. Not more then 20 new external links
551 if ($this->numLinks($newtext) - $this->numLinks($oldtext) >= 20) {
553 $this->tokens['PAGE_LOCKED_MESSAGE'] =
554 HTML($this->getSpamMessage(),
555 HTML::p(HTML::em(_("Too many external links."))));
558 // 2. external babycart (SpamAssassin) check
559 // This will probably prevent from discussing sex or viagra related topics. So beware.
560 if (ENABLE_SPAMASSASSIN) {
561 $user = $request->getUser();
562 include_once("lib/spam_babycart.php");
563 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"), $user->getId())) {
565 if (is_array($babycart))
566 $this->tokens['PAGE_LOCKED_MESSAGE'] =
567 HTML($this->getSpamMessage(),
568 HTML::p(HTML::em(_("SpamAssassin reports: ", join("\n", $babycart)))));
575 /** Number of external links in the wikitext
577 function numLinks(&$text) {
578 return substr_count($text, "http://");
581 /** Header of the Anti Spam message
583 function getSpamMessage () {
585 HTML(HTML::h2(_("Spam Prevention")),
586 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
588 _("Sorry for the inconvenience.")),
592 function getPreview () {
593 include_once('lib/PageType.php');
594 $this->_content = $this->getContent();
595 return new TransformedText($this->page, $this->_content, $this->meta);
598 // possibly convert HTMLAREA content back to Wiki markup
599 function getContent () {
601 $xml_output = Edit_HtmlArea_ConvertAfter($this->_content);
602 $this->_content = join("",$xml_output->_content);
603 return $this->_content;
605 return $this->_content;
609 function getLockedMessage () {
611 HTML(HTML::h2(_("Page Locked")),
612 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
613 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
614 HTML::p(_("Sorry for the inconvenience.")));
617 function getConflictMessage ($unresolved = false) {
619 xgettext only knows about c/c++ line-continuation strings
620 it does not know about php's dot operator.
621 We want to translate this entire paragraph as one string, of course.
624 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
627 $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.",
628 "<<<<<<< ". _("Your version"),
629 ">>>>>>> ". _("Other version")));
631 $message = HTML::p(_("Please check it through before saving."));
635 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
636 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
638 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
639 HTML::li(_("Save your updated changes.")));
642 HTML(HTML::h2(_("Conflicting Edits!")),
643 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
644 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.")),
649 function getTextArea () {
650 $request = &$this->request;
652 // wrap=virtual is not HTML4, but without it NS4 doesn't wrap
654 $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
656 $html = $this->getPreview();
657 $this->_wikicontent = $this->_content;
658 $this->_content = $html->asXML();
661 /** <textarea wrap="virtual"> is not valid xhtml but Netscape 4 requires it
662 * to wrap long lines.
664 $textarea = HTML::textarea(array('class' => 'wikiedit',
665 'name' => 'edit[content]',
666 'id' => 'edit[content]',
667 'rows' => $request->getPref('editHeight'),
668 'cols' => $request->getPref('editWidth'),
669 'readonly' => (bool) $readonly),
672 $textarea->setAttr('wrap', 'virtual');
674 return Edit_HtmlArea_Textarea($textarea,$this->_wikicontent,'edit[content]');
680 * TODO: maybe support an uploadfile button.
682 function getFormElements () {
683 $request = &$this->request;
684 $page = &$this->page;
686 $h = array('action' => 'edit',
687 'pagename' => $page->getName(),
688 'version' => $this->version,
689 'edit[pagetype]' => $this->meta['pagetype'],
690 'edit[current_version]' => $this->_currentVersion);
692 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
693 $el['EDIT_TEXTAREA'] = $this->getTextArea();
695 = HTML::input(array('type' => 'text',
696 'class' => 'wikitext',
697 'name' => 'edit[summary]',
700 'value' => $this->meta['summary']));
702 = HTML::input(array('type' => 'checkbox',
703 'name' => 'edit[minor_edit]',
704 'checked' => (bool) $this->meta['is_minor_edit']));
706 = HTML::input(array('type' => 'checkbox',
707 'name' => 'edit[markup]',
709 'checked' => $this->meta['markup'] < 2.0,
710 'id' => 'useOldMarkup',
711 'onclick' => 'showOldMarkupRules(this.checked)'));
714 = HTML::input(array('type' => 'checkbox',
715 'name' => 'edit[locked]',
716 'disabled' => (bool) !$this->user->isadmin(),
717 'checked' => (bool) $this->locked));
719 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
722 //if (!$this->isConcurrentUpdate() && $this->canEdit())
723 $el['SAVE_B'] = Button('submit:edit[save]', _("Save"), 'wikiaction');
725 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
730 function _redirectToBrowsePage() {
731 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
734 function _restoreState () {
735 $request = &$this->request;
737 $posted = $request->getArg('edit');
738 $request->setArg('edit', false);
740 if (!$posted || !$request->isPost()
741 || $request->getArg('action') != 'edit')
744 if (!isset($posted['content']) || !is_string($posted['content']))
746 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
747 rtrim($posted['content']));
748 $this->_content = $this->getContent();
750 $this->_currentVersion = (int) $posted['current_version'];
752 if ($this->_currentVersion < 0)
754 if ($this->_currentVersion > $this->current->getVersion())
755 return false; // FIXME: some kind of warning?
757 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
758 $meta['markup'] = $is_old_markup ? false : 2.0;
759 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
760 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
761 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
762 $this->meta = array_merge($this->meta, $meta);
763 $this->locked = !empty($posted['locked']);
765 if (!empty($posted['preview']))
766 $this->editaction = 'preview';
767 elseif (!empty($posted['save']))
768 $this->editaction = 'save';
770 $this->editaction = 'edit';
775 function _initializeState () {
776 $request = &$this->request;
777 $current = &$this->current;
778 $selected = &$this->selected;
779 $user = &$this->user;
782 NoSuchRevision($request, $this->page, $this->version); // noreturn
784 $this->_currentVersion = $current->getVersion();
785 $this->_content = $selected->getPackedContent();
787 $this->meta['summary'] = '';
788 $this->locked = $this->page->get('locked');
790 // If author same as previous author, default minor_edit to on.
791 $age = $this->meta['mtime'] - $current->get('mtime');
792 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
793 && $current->get('author') == $user->getId()
796 // Default for new pages is new-style markup.
797 if ($selected->hasDefaultContents())
798 $is_new_markup = true;
800 $is_new_markup = $selected->get('markup') >= 2.0;
802 $this->meta['markup'] = $is_new_markup ? 2.0: false;
803 $this->meta['pagetype'] = $selected->get('pagetype');
804 $this->editaction = 'edit';
808 class LoadFileConflictPageEditor
811 function editPage ($saveFailed = true) {
812 $tokens = &$this->tokens;
814 if (!$this->canEdit()) {
815 if ($this->isInitialEdit())
816 return $this->viewSource();
817 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
819 elseif ($this->editaction == 'save') {
820 if ($this->savePage())
821 return true; // Page saved.
825 if ($saveFailed || $this->isConcurrentUpdate())
827 // Get the text of the original page, and the two conflicting edits
828 // The diff class takes arrays as input. So retrieve content as
829 // an array, or convert it as necesary.
830 $orig = $this->page->getRevision($this->_currentVersion);
831 $this_content = explode("\n", $this->_content);
832 $other_content = $this->current->getContent();
833 include_once("lib/diff.php");
834 $diff2 = new Diff($other_content, $this_content);
835 $context_lines = max(4, count($other_content) + 1,
836 count($this_content) + 1);
837 $fmt = new BlockDiffFormatter($context_lines);
839 $this->_content = $fmt->format($diff2);
840 // FIXME: integrate this into class BlockDiffFormatter
841 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
843 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
846 $this->_currentVersion = $this->current->getVersion();
847 $this->version = $this->_currentVersion;
848 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
851 if ($this->editaction == 'preview')
852 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
854 // FIXME: NOT_CURRENT_MESSAGE?
856 $tokens = array_merge($tokens, $this->getFormElements());
858 return $this->output('editpage', _("Merge and Edit: %s"));
859 // FIXME: this doesn't display
862 function output ($template, $title_fs) {
863 $selected = &$this->selected;
864 $current = &$this->current;
866 if ($selected && $selected->getVersion() != $current->getVersion()) {
868 $pagelink = WikiLink($selected);
872 $pagelink = WikiLink($this->page);
875 $title = new FormattedText ($title_fs, $pagelink);
876 $template = Template($template, $this->tokens);
878 //GeneratePage($template, $title, $rev);
882 function getConflictMessage () {
883 $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.",
886 HTML::p(_("Please check it through before saving."))));
892 $Log: not supported by cvs2svn $
893 Revision 1.84 2004/12/04 12:58:26 rurban
894 enable babycart Blog::SpamAssassin module on ENABLE_SPAMASSASSIN=true
895 (currently only for php >= 4.3.0)
897 Revision 1.83 2004/12/04 11:55:39 rurban
898 First simple AntiSpam prevention:
899 No more than 20 new http:// links allowed
901 Revision 1.82 2004/11/30 22:21:56 rurban
902 changed gif to optimized (pngout) png
904 Revision 1.81 2004/11/29 17:57:27 rurban
905 translated pulldown buttons
907 Revision 1.80 2004/11/25 17:20:51 rurban
908 and again a couple of more native db args: backlinks
910 Revision 1.79 2004/11/21 11:59:20 rurban
911 remove final \n to be ob_cache independent
913 Revision 1.78 2004/11/16 17:57:45 rurban
914 fix search&replace button
915 use new addTagButton machinery
916 new showPulldown for categories, TODO: in a seperate request
918 Revision 1.77 2004/11/15 15:52:35 rurban
921 Revision 1.76 2004/11/15 15:37:34 rurban
923 don't use document.write for replace, otherwise self.opener is not defined.
925 Revision 1.75 2004/09/16 08:00:52 rurban
928 Revision 1.74 2004/07/03 07:36:28 rurban
929 do not get unneccessary content
931 Revision 1.73 2004/06/16 21:23:44 rurban
932 fixed non-object fatal #215
934 Revision 1.72 2004/06/14 11:31:37 rurban
935 renamed global $Theme to $WikiTheme (gforge nameclash)
936 inherit PageList default options from PageList
937 default sortby=pagename
938 use options in PageList_Selectable (limit, sortby, ...)
939 added action revert, with button at action=diff
940 added option regex to WikiAdminSearchReplace
942 Revision 1.71 2004/06/03 18:06:29 rurban
943 fix file locking issues (only needed on write)
944 fixed immediate LANG and THEME in-session updates if not stored in prefs
945 advanced editpage toolbars (search & replace broken)
947 Revision 1.70 2004/06/02 20:47:47 rurban
948 dont use the wikiaction class
950 Revision 1.69 2004/06/02 10:17:56 rurban
951 integrated search/replace into toolbar
952 added save+preview buttons
954 Revision 1.68 2004/06/01 15:28:00 rurban
955 AdminUser only ADMIN_USER not member of Administrators
956 some RateIt improvements by dfrankow
959 Revision _1.6 2004/05/26 15:48:00 syilek
960 fixed problem with creating page with slashes from one true page
962 Revision _1.5 2004/05/25 16:51:53 syilek
963 added ability to create a page from the category page and not have to edit it
965 Revision 1.67 2004/05/27 17:49:06 rurban
966 renamed DB_Session to DbSession (in CVS also)
967 added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
968 remove leading slash in error message
969 added force_unlock parameter to File_Passwd (no return on stale locks)
970 fixed adodb session AffectedRows
971 added FileFinder helpers to unify local filenames and DATA_PATH names
972 editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
974 Revision 1.66 2004/04/29 23:25:12 rurban
975 re-ordered locale init (as in 1.3.9)
976 fixed loadfile with subpages, and merge/restore anyway
979 Revision 1.65 2004/04/18 01:11:52 rurban
980 more numeric pagename fixes.
981 fixed action=upload with merge conflict warnings.
982 charset changed from constant to global (dynamic utf-8 switching)
984 Revision 1.64 2004/04/06 19:48:56 rurban
985 temp workaround for action=edit AddComment form
987 Revision 1.63 2004/03/24 19:39:02 rurban
988 php5 workaround code (plus some interim debugging code in XmlElement)
989 php5 doesn't work yet with the current XmlElement class constructors,
990 WikiUserNew does work better than php4.
991 rewrote WikiUserNew user upgrading to ease php5 update
992 fixed pref handling in WikiUserNew
993 added Email Notification
994 added simple Email verification
995 removed emailVerify userpref subclass: just a email property
996 changed pref binary storage layout: numarray => hash of non default values
997 print optimize message only if really done.
998 forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string.
999 prefs should be stored in db or homepage, besides the current session.
1001 Revision 1.62 2004/03/17 18:41:05 rurban
1002 initial_content and template support for CreatePage
1004 Revision 1.61 2004/03/12 20:59:17 rurban
1005 important cookie fix by Konstantin Zadorozhny
1006 new editpage feature: JS_SEARCHREPLACE
1008 Revision 1.60 2004/02/15 21:34:37 rurban
1009 PageList enhanced and improved.
1010 fixed new WikiAdmin... plugins
1011 editpage, Theme with exp. htmlarea framework
1012 (htmlarea yet committed, this is really questionable)
1013 WikiUser... code with better session handling for prefs
1014 enhanced UserPreferences (again)
1015 RecentChanges for show_deleted: how should pages be deleted then?
1017 Revision 1.59 2003/12/07 20:35:26 carstenklapp
1018 Bugfix: Concurrent updates broken since after 1.3.4 release: Fatal
1019 error: Call to undefined function: gettransformedcontent() in
1020 /home/groups/p/ph/phpwiki/htdocs/phpwiki2/lib/editpage.php on line
1023 Revision 1.58 2003/03/10 18:25:22 dairiki
1024 Bug/typo fix. If you use the edit page to un/lock a page, it
1025 failed with: Fatal error: Call to a member function on a
1026 non-object in editpage.php on line 136
1028 Revision 1.57 2003/02/26 03:40:22 dairiki
1029 New action=create. Essentially the same as action=edit, except that if the
1030 page already exists, it falls back to action=browse.
1032 This is for use in the "question mark" links for unknown wiki words
1033 to avoid problems and confusion when following links from stale pages.
1034 (If the "unknown page" has been created in the interim, the user probably
1035 wants to view the page before editing it.)
1037 Revision 1.56 2003/02/21 18:07:14 dairiki
1038 Minor, nitpicky, currently inconsequential changes.
1040 Revision 1.55 2003/02/21 04:10:58 dairiki
1041 Fixes for new cached markup.
1042 Some minor code cleanups.
1044 Revision 1.54 2003/02/16 19:47:16 dairiki
1045 Update WikiDB timestamp when editing or deleting pages.
1047 Revision 1.53 2003/02/15 23:20:27 dairiki
1048 Redirect back to browse current version of page upon save,
1049 even when no changes were made.
1051 Revision 1.52 2003/01/03 22:22:00 carstenklapp
1052 Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
1054 Revision 1.51 2003/01/03 02:43:26 carstenklapp
1055 New class LoadFileConflictPageEditor, for merging / comparing a loaded
1056 pgsrc file with an existing page.
1063 // c-basic-offset: 4
1064 // c-hanging-comment-ender-p: nil
1065 // indent-tabs-mode: nil