2 rcs_id('$Id: editpage.php,v 1.90 2005-01-22 12:46:15 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'))
57 and $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;
74 if (isset($this->request->args['pref']['editWidth'])
75 and ($this->request->getPref('editWidth') != $this->request->args['pref']['editWidth'])) {
76 $this->request->_prefs->set('editWidth', $this->request->args['pref']['editWidth']);
78 if (isset($this->request->args['pref']['editHeight'])
79 and ($this->request->getPref('editHeight') != $this->request->args['pref']['editHeight'])) {
80 $this->request->_prefs->set('editHeight', $this->request->args['pref']['editHeight']);
83 if (! $this->canEdit()) {
84 if ($this->isInitialEdit())
85 return $this->viewSource();
86 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
88 elseif ($this->request->getArg('save_and_redirect_to') != "") {
89 if ($this->savePage()) {
91 $this->request->redirect(WikiURL($this->request->getArg('save_and_redirect_to')));
92 return true; // Page saved.
96 elseif ($this->editaction == 'save') {
97 if ($this->savePage()) {
98 return true; // Page saved.
103 if ($saveFailed and $this->isConcurrentUpdate())
105 // Get the text of the original page, and the two conflicting edits
106 // The diff3 class takes arrays as input. So retrieve content as
107 // an array, or convert it as necesary.
108 $orig = $this->page->getRevision($this->_currentVersion);
109 // FIXME: what if _currentVersion has be deleted?
110 $orig_content = $orig->getContent();
111 $this_content = explode("\n", $this->_content);
112 $other_content = $this->current->getContent();
113 include_once("lib/diff3.php");
114 $diff = new diff3($orig_content, $this_content, $other_content);
115 $output = $diff->merged_output(_("Your version"), _("Other version"));
116 // Set the content of the textarea to the merged diff
117 // output, and update the version
118 $this->_content = implode ("\n", $output);
119 $this->_currentVersion = $this->current->getVersion();
120 $this->version = $this->_currentVersion;
121 $unresolved = $diff->ConflictingBlocks;
122 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage($unresolved);
123 } elseif ($saveFailed) {
124 $tokens['CONCURRENT_UPDATE_MESSAGE'] =
125 HTML(HTML::h2(_("Some internal editing error")),
126 HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
127 HTML::p(HTML::em(_("&version=-1 might help."))));
130 if ($this->editaction == 'edit_convert')
131 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
132 if ($this->editaction == 'preview')
133 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
135 // FIXME: NOT_CURRENT_MESSAGE?
136 $tokens = array_merge($tokens, $this->getFormElements());
138 //FIXME: enable Undo button for all other buttons also, not only the search/replace button
139 if (defined('JS_SEARCHREPLACE') and JS_SEARCHREPLACE) {
140 $tokens['JS_SEARCHREPLACE'] = 1;
141 $undo_btn = $WikiTheme->getImageURL("ed_undo.png");
142 $undo_d_btn = $WikiTheme->getImageURL("ed_undo_d.png");
143 // JS_SEARCHREPLACE from walterzorn.de
144 $WikiTheme->addMoreHeaders(Javascript("
145 var f, sr_undo, replacewin, undo_buffer=new Array(), undo_buffer_index=0;
147 function define_f() {
148 f=document.getElementById('editpage');
149 f.editarea=document.getElementById('edit[content]');
150 sr_undo=document.getElementById('sr_undo');
154 function undo_enable(bool) {
156 sr_undo.src='".$undo_btn."';
160 sr_undo.disabled = false;
162 sr_undo.src='".$undo_d_btn."';
166 sr_undo.disabled = true;
167 if(sr_undo.blur) sr_undo.blur();
171 replacewin = window.open('','','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,height=90,width=450');
172 replacewin.window.document.write('<html><head><title>"
173 ._("Search & Replace")
174 ."</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\">'+'"
176 .":</td><td align=\"left\"><input type=\"text\" name=\"ein\" size=\"45\" maxlength=\"500\"></td></tr><tr><td align=\"right\">'+' "
178 .":</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=\" "
180 ." \" onclick=\"if(self.opener)self.opener.do_replace(); return false;\"> <input type=\"button\" value=\""
182 ."\" onclick=\"self.close(); return false;\"></td></tr></table></center></form></body></html>');
183 replacewin.window.document.close();
186 function do_replace() {
187 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;
188 if (ein==''||ein==null) {
189 if (replacewin) replacewin.window.document.forms[0].ein.focus();
192 var z_repl=txt.match(ein)? txt.match(ein).length : 0;
193 txt=txt.replace(ein,aus);
194 ein=ein.toString().substring(1,ein.toString().length-2);
195 result(z_repl, 'Substring \"'+ein+'\" found '+z_repl+' times. Replace with \"'+aus+'\"?', txt, 'String \"'+ein+'\" not found.');
196 replacewin.window.focus();
197 replacewin.window.document.forms[0].ein.focus();
200 function result(zahl,frage,txt,alert_txt) {
202 if(window.confirm(frage)==true) {
203 f.editarea.value=txt;
207 } else alert(alert_txt);
210 if(undo_buffer_index==0) return;
211 else if(undo_buffer_index>0) {
212 f.editarea.value=undo_buffer[undo_buffer_index-1];
213 undo_buffer[undo_buffer_index]=null;
215 if(undo_buffer_index==0) {
217 _("Operation undone")
223 //save a snapshot in the undo buffer (unused)
224 function undo_save() {
225 undo_buffer[undo_buffer_index]=f.editarea.value;
230 $WikiTheme->addMoreAttr('body'," onload='define_f()'");
232 $WikiTheme->addMoreAttr('body',"document.getElementById('edit[content]').editarea.focus()");
234 if (defined('ENABLE_EDIT_TOOLBAR') and ENABLE_EDIT_TOOLBAR) {
235 $WikiTheme->addMoreHeaders(JavaScript('',array('src' => $WikiTheme->_findData("toolbar.js"))));
236 $tokens['EDIT_TOOLBAR'] = $this->toolbar();
238 $tokens['EDIT_TOOLBAR'] = '';
241 return $this->output('editpage', _("Edit: %s"));
244 function toolbar () {
248 "image"=>"ed_format_bold.png",
251 "sample"=>_("Bold text"),
252 "tip"=>_("Bold text")),
253 array("image"=>"ed_format_italic.png",
256 "sample"=>_("Italic text"),
257 "tip"=>_("Italic text")),
258 array("image"=>"ed_pagelink.png",
261 "sample"=>_("optional label | PageName"),
262 "tip"=>_("Link to page")),
263 array("image"=>"ed_link.png",
266 "sample"=>_("optional label | http://www.example.com"),
267 "tip"=>_("External link (remember http:// prefix)")),
268 array("image"=>"ed_headline.png",
271 "sample"=>_("Headline text"),
272 "tip"=>_("Level 1 headline")),
273 array("image"=>"ed_image.png",
276 "sample"=>_("Example.jpg"),
277 "tip"=>_("Embedded image")),
278 array("image"=>"ed_nowiki.png",
279 "open"=>"\\n\\<verbatim\\>\\n",
280 "close"=>"\\n\\</verbatim\\>\\n",
281 "sample"=>_("Insert non-formatted text here"),
282 "tip"=>_("Ignore wiki formatting")),
283 array("image"=>"ed_sig.png",
284 "open" => " --" . $GLOBALS['request']->_user->UserName(),
287 "tip"=>_("Your signature")),
288 array("image"=>"ed_hr.png",
289 "open"=>"\\n----\\n",
292 "tip"=>_("Horizontal line"))
294 $toolbar = "document.writeln(\"<div class=\\\"edit-toolbar\\\" id=\\\"toolbar\\\">\");\n";
296 $btn = new SubmitImageButton(_("Save"), "edit[save]", 'toolbar', $WikiTheme->getImageURL("ed_save.png"));
297 $btn->addTooltip(_("Save"));
298 $toolbar.='document.writeln("'.addslashes($btn->asXml()).'");'."\n";
299 $btn = new SubmitImageButton(_("Preview"), "edit[preview]", 'toolbar', $WikiTheme->getImageURL("ed_preview.png"));
300 $btn->addTooltip(_("Preview"));
301 $toolbar.='document.writeln("'.addslashes($btn->asXml()).'");'."\n";
303 foreach ($toolarray as $tool) {
304 $image = $WikiTheme->getImageURL($tool["image"]);
305 $open = $tool["open"];
306 $close = $tool["close"];
307 $sample = addslashes( $tool["sample"] );
308 // Note that we use the tip both for the ALT tag and the TITLE tag of the image.
309 // Older browsers show a "speedtip" type message only for ALT.
310 // Ideally these should be different, realistically they
311 // probably don't need to be.
312 $tip = addslashes( $tool["tip"] );
313 $toolbar.="addTagButton('$image','$tip','$open','$close','$sample');\n";
315 $toolbar.="addInfobox('" . addslashes( _("Click a button to get an example text") ) . "');\n";
317 if (JS_SEARCHREPLACE) {
318 $undo_d_btn = $WikiTheme->getImageURL("ed_undo_d.png");
319 //$redo_btn = $WikiTheme->getImageURL("ed_redo.png");
320 $sr_btn = $WikiTheme->getImageURL("ed_replace.png");
321 $sr_html = HTML(HTML::img(array('class'=>"toolbar",
324 'title'=>_("Undo Search & Replace"),
325 'alt' =>_("Undo Search & Replace"),
326 'disabled'=>"disabled",
328 'onfocus' =>"if(this.blur && undo_buffer_index==0) this.blur()",
329 'onclick' =>"do_undo()")),
330 HTML::img(array('class'=>"toolbar",
332 'alt' =>_("Search & Replace"),
333 'title'=>_("Search & Replace"),
334 'onclick'=>"replace()")));
339 // Button to generate pagenames, display in extra window as pulldown and insert
340 // Button to generate plugins, display in extra window as pulldown and insert
341 // Button to generate categories, display in extra window as pulldown and insert
342 if (DEBUG and JS_SEARCHREPLACE) {
343 //TODO: delegate this calculation to a seperate pulldown action request
344 require_once('lib/TextSearchQuery.php');
345 $dbi =& $GLOBALS['request']->_dbi;
346 $pages = $dbi->titleSearch(new TextSearchQuery(''.
347 _("Category").' OR '.
349 if ($pages->count()) {
350 $categories = array();
351 while ($p = $pages->next()){
352 $categories[] = $p->getName();
354 $more_buttons = HTML::img(array('class'=>"toolbar",
355 'src' => $WikiTheme->getImageURL("ed_category.png"),
356 'title'=>_("AddCategory"),
357 'onclick'=>"showPulldown('".
358 _("Insert Categories (double-click)")
359 ."',['".join("','",$categories)."'],'"
363 if ($sr_html) $sr_html = HTML($sr_html, $more_buttons);
364 else $sr_html = $more_buttons;
367 $toolbar_end = "document.writeln(\"</div>\");";
368 // don't use document.write for replace, otherwise self.opener is not defined.
370 return HTML(Javascript($toolbar),
372 Javascript($toolbar_end));
374 return HTML(Javascript($toolbar . $toolbar_end));
377 function output ($template, $title_fs) {
379 $selected = &$this->selected;
380 $current = &$this->current;
382 if ($selected && $selected->getVersion() != $current->getVersion()) {
384 $pagelink = WikiLink($selected);
388 $pagelink = WikiLink($this->page);
392 $title = new FormattedText ($title_fs, $pagelink);
393 if (USE_HTMLAREA and $template == 'editpage') {
394 $WikiTheme->addMoreHeaders(Edit_HtmlArea_Head());
395 //$tokens['PAGE_SOURCE'] = Edit_HtmlArea_ConvertBefore($this->_content);
397 $template = Template($template, $this->tokens);
398 GeneratePage($template, $title, $rev);
403 function viewSource () {
404 assert($this->isInitialEdit());
405 assert($this->selected);
407 $this->tokens['PAGE_SOURCE'] = $this->_content;
408 return $this->output('viewsource', _("View Source: %s"));
411 function updateLock() {
412 if ((bool)$this->page->get('locked') == (bool)$this->locked)
413 return false; // Not changed.
415 if (!$this->user->isAdmin()) {
416 // FIXME: some sort of message
417 return false; // not allowed.
420 $this->page->set('locked', (bool)$this->locked);
421 $this->tokens['LOCK_CHANGED_MSG']
422 = $this->locked ? _("Page now locked.") : _("Page now unlocked.");
424 return true; // lock changed.
427 function savePage () {
428 $request = &$this->request;
430 if ($this->isUnchanged()) {
431 // Allow admin lock/unlock even if
432 // no text changes were made.
433 if ($this->updateLock()) {
434 $dbi = $request->getDbh();
437 // Save failed. No changes made.
438 $this->_redirectToBrowsePage();
439 // user will probably not see the rest of this...
440 include_once('lib/display.php');
441 // force browse of current version:
442 $request->setArg('version', false);
443 displayPage($request, 'nochanges');
447 if ($this->isSpam()) {
450 // Save failed. No changes made.
451 $this->_redirectToBrowsePage();
452 // user will probably not see the rest of this...
453 include_once('lib/display.php');
454 // force browse of current version:
455 $request->setArg('version', false);
456 displayPage($request, 'nochanges');
461 $page = &$this->page;
463 // Include any meta-data from original page version which
464 // has not been explicitly updated.
465 // (Except don't propagate pgsrc_version --- moot for now,
466 // because at present it never gets into the db...)
467 $meta = $this->selected->getMetaData();
468 unset($meta['pgsrc_version']);
469 $meta = array_merge($meta, $this->meta);
472 $this->_content = $this->getContent();
473 $newrevision = $page->save($this->_content,
476 : $this->_currentVersion + 1,
479 if (!isa($newrevision, 'WikiDB_PageRevision')) {
480 // Save failed. (Concurrent updates).
484 // New contents successfully saved...
487 // Clean out archived versions of this page.
488 include_once('lib/ArchiveCleaner.php');
489 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
490 $cleaner->cleanPageRevisions($page);
492 /* generate notification emails done in WikiDB::save to catch all direct calls
495 $dbi = $request->getDbh();
496 $warnings = $dbi->GenericWarnings();
500 if (empty($warnings) && ! $WikiTheme->getImageURL('signature')) {
501 // Do redirect to browse page if no signature has
502 // been defined. In this case, the user will most
503 // likely not see the rest of the HTML we generate
505 $this->_redirectToBrowsePage();
508 // Force browse of current page version.
509 $request->setArg('version', false);
510 //$request->setArg('action', false);
512 $template = Template('savepage', $this->tokens);
513 $template->replace('CONTENT', $newrevision->getTransformedContent());
514 if (!empty($warnings))
515 $template->replace('WARNINGS', $warnings);
517 $pagelink = WikiLink($page);
519 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
523 function isConcurrentUpdate () {
524 assert($this->current->getVersion() >= $this->_currentVersion);
525 return $this->current->getVersion() != $this->_currentVersion;
528 function canEdit () {
529 return !$this->page->get('locked') || $this->user->isAdmin();
532 function isInitialEdit () {
533 return $this->_initialEdit;
536 function isUnchanged () {
537 $current = &$this->current;
539 if ($this->meta['markup'] != $current->get('markup'))
542 return $this->_content == $current->getPackedContent();
546 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
547 * Need to check dynamically some blacklist wikipage settings
548 * (plugin WikiAccessRestrictions) and some static blacklist.
550 * More then 20 new external links
551 * content patterns by babycart (only php >= 4.3 for now)
558 $current = &$this->current;
559 $request = &$this->request;
561 $oldtext = $current->getPackedContent();
562 $newtext =& $this->_content;
563 // 1. Not more then 20 new external links
564 if ($this->numLinks($newtext) - $this->numLinks($oldtext) >= 20) {
566 $this->tokens['PAGE_LOCKED_MESSAGE'] =
567 HTML($this->getSpamMessage(),
568 HTML::p(HTML::em(_("Too many external links."))));
571 // 2. external babycart (SpamAssassin) check
572 // This will probably prevent from discussing sex or viagra related topics. So beware.
573 if (ENABLE_SPAMASSASSIN) {
574 $user = $request->getUser();
575 include_once("lib/spam_babycart.php");
576 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
579 if (is_array($babycart))
580 $this->tokens['PAGE_LOCKED_MESSAGE'] =
581 HTML($this->getSpamMessage(),
582 HTML::p(HTML::em(_("SpamAssassin reports: ",
583 join("\n", $babycart)))));
590 /** Number of external links in the wikitext
592 function numLinks(&$text) {
593 return substr_count($text, "http://");
596 /** Header of the Anti Spam message
598 function getSpamMessage () {
600 HTML(HTML::h2(_("Spam Prevention")),
601 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
603 _("Sorry for the inconvenience.")),
607 function getPreview () {
608 include_once('lib/PageType.php');
609 $this->_content = $this->getContent();
610 return new TransformedText($this->page, $this->_content, $this->meta);
613 function getConvertedPreview () {
614 include_once('lib/PageType.php');
615 $this->_content = $this->getContent();
616 $this->meta['markup'] = 2.0;
617 $this->_content = ConvertOldMarkup($this->_content);
618 return new TransformedText($this->page, $this->_content, $this->meta);
621 // possibly convert HTMLAREA content back to Wiki markup
622 function getContent () {
624 $xml_output = Edit_HtmlArea_ConvertAfter($this->_content);
625 $this->_content = join("", $xml_output->_content);
626 return $this->_content;
628 return $this->_content;
632 function getLockedMessage () {
634 HTML(HTML::h2(_("Page Locked")),
635 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
636 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
637 HTML::p(_("Sorry for the inconvenience.")));
640 function getConflictMessage ($unresolved = false) {
642 xgettext only knows about c/c++ line-continuation strings
643 it does not know about php's dot operator.
644 We want to translate this entire paragraph as one string, of course.
647 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
650 $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.",
651 "<<<<<<< ". _("Your version"),
652 ">>>>>>> ". _("Other version")));
654 $message = HTML::p(_("Please check it through before saving."));
658 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
659 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
661 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
662 HTML::li(_("Save your updated changes.")));
665 HTML(HTML::h2(_("Conflicting Edits!")),
666 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
667 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.")),
672 function getTextArea () {
673 $request = &$this->request;
675 // wrap=virtual is not HTML4, but without it NS4 doesn't wrap
677 $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
679 $html = $this->getPreview();
680 $this->_wikicontent = $this->_content;
681 $this->_content = $html->asXML();
684 /** <textarea wrap="virtual"> is not valid xhtml but Netscape 4 requires it
685 * to wrap long lines.
687 $textarea = HTML::textarea(array('class' => 'wikiedit',
688 'name' => 'edit[content]',
689 'id' => 'edit[content]',
690 'rows' => $request->getPref('editHeight'),
691 'cols' => $request->getPref('editWidth'),
692 'readonly' => (bool) $readonly),
695 $textarea->setAttr('wrap', 'virtual');
697 return Edit_HtmlArea_Textarea($textarea,$this->_wikicontent,'edit[content]');
702 function getFormElements () {
704 $request = &$this->request;
705 $page = &$this->page;
707 $h = array('action' => 'edit',
708 'pagename' => $page->getName(),
709 'version' => $this->version,
710 'edit[pagetype]' => $this->meta['pagetype'],
711 'edit[current_version]' => $this->_currentVersion);
713 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
714 $el['EDIT_TEXTAREA'] = $this->getTextArea();
716 = HTML::input(array('type' => 'text',
717 'class' => 'wikitext',
718 'id' => 'edit[summary]',
719 'name' => 'edit[summary]',
722 'value' => $this->meta['summary']));
724 = HTML::input(array('type' => 'checkbox',
725 'name' => 'edit[minor_edit]',
726 'id' => 'edit[minor_edit]',
727 'checked' => (bool) $this->meta['is_minor_edit']));
729 = HTML::input(array('type' => 'checkbox',
730 'name' => 'edit[markup]',
732 'checked' => $this->meta['markup'] < 2.0,
733 'id' => 'useOldMarkup',
734 'onclick' => 'showOldMarkupRules(this.checked)'));
735 $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0)
736 ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
738 = HTML::input(array('type' => 'checkbox',
739 'name' => 'edit[locked]',
740 'id' => 'edit[locked]',
741 'disabled' => (bool) !$this->user->isadmin(),
742 'checked' => (bool) $this->locked));
744 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
747 //if (!$this->isConcurrentUpdate() && $this->canEdit())
748 $el['SAVE_B'] = Button('submit:edit[save]', _("Save"), 'wikiaction');
750 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
752 $el['WIDTH_PREF'] = HTML::input(array('type' => 'text',
755 'class' => "numeric",
756 'name' => 'pref[editWidth]',
757 'id' => 'pref[editWidth]',
758 'value' => $request->getPref('editWidth'),
759 'onchange' => 'this.form.submit();'));
760 $el['HEIGHT_PREF'] = HTML::input(array('type' => 'text',
763 'class' => "numeric",
764 'name' => 'pref[editHeight]',
765 'id' => 'pref[editHeight]',
766 'value' => $request->getPref('editHeight'),
767 'onchange' => 'this.form.submit();'));
768 $el['SEP'] = $WikiTheme->getButtonSeparator();
772 function _redirectToBrowsePage() {
773 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
776 function _restoreState () {
777 $request = &$this->request;
779 $posted = $request->getArg('edit');
780 $request->setArg('edit', false);
782 if (!$posted || !$request->isPost()
783 || $request->getArg('action') != 'edit')
786 if (!isset($posted['content']) || !is_string($posted['content']))
788 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
789 rtrim($posted['content']));
790 $this->_content = $this->getContent();
792 $this->_currentVersion = (int) $posted['current_version'];
794 if ($this->_currentVersion < 0)
796 if ($this->_currentVersion > $this->current->getVersion())
797 return false; // FIXME: some kind of warning?
799 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
800 $meta['markup'] = $is_old_markup ? false : 2.0;
801 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
802 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
803 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
804 $this->meta = array_merge($this->meta, $meta);
805 $this->locked = !empty($posted['locked']);
807 if (!empty($posted['preview']))
808 $this->editaction = 'preview';
809 elseif (!empty($posted['save']))
810 $this->editaction = 'save';
811 elseif (!empty($posted['edit_convert']))
812 $this->editaction = 'edit_convert';
814 $this->editaction = 'edit';
819 function _initializeState () {
820 $request = &$this->request;
821 $current = &$this->current;
822 $selected = &$this->selected;
823 $user = &$this->user;
826 NoSuchRevision($request, $this->page, $this->version); // noreturn
828 $this->_currentVersion = $current->getVersion();
829 $this->_content = $selected->getPackedContent();
831 $this->locked = $this->page->get('locked');
833 // If author same as previous author, default minor_edit to on.
834 $age = $this->meta['mtime'] - $current->get('mtime');
835 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
836 && $current->get('author') == $user->getId()
839 // Default for new pages is new-style markup.
840 if ($selected->hasDefaultContents())
841 $is_new_markup = true;
843 $is_new_markup = $selected->get('markup') >= 2.0;
845 $this->meta['markup'] = $is_new_markup ? 2.0: false;
846 $this->meta['pagetype'] = $selected->get('pagetype');
847 if ($this->meta['pagetype'] == 'wikiblog')
848 $this->meta['summary'] = $selected->get('summary'); // keep blog title
850 $this->meta['summary'] = '';
851 $this->editaction = 'edit';
855 class LoadFileConflictPageEditor
858 function editPage ($saveFailed = true) {
859 $tokens = &$this->tokens;
861 if (!$this->canEdit()) {
862 if ($this->isInitialEdit())
863 return $this->viewSource();
864 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
866 elseif ($this->editaction == 'save') {
867 if ($this->savePage())
868 return true; // Page saved.
872 if ($saveFailed || $this->isConcurrentUpdate())
874 // Get the text of the original page, and the two conflicting edits
875 // The diff class takes arrays as input. So retrieve content as
876 // an array, or convert it as necesary.
877 $orig = $this->page->getRevision($this->_currentVersion);
878 $this_content = explode("\n", $this->_content);
879 $other_content = $this->current->getContent();
880 include_once("lib/diff.php");
881 $diff2 = new Diff($other_content, $this_content);
882 $context_lines = max(4, count($other_content) + 1,
883 count($this_content) + 1);
884 $fmt = new BlockDiffFormatter($context_lines);
886 $this->_content = $fmt->format($diff2);
887 // FIXME: integrate this into class BlockDiffFormatter
888 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
890 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
893 $this->_currentVersion = $this->current->getVersion();
894 $this->version = $this->_currentVersion;
895 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
898 if ($this->editaction == 'edit_convert')
899 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
900 if ($this->editaction == 'preview')
901 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
903 // FIXME: NOT_CURRENT_MESSAGE?
905 $tokens = array_merge($tokens, $this->getFormElements());
907 return $this->output('editpage', _("Merge and Edit: %s"));
908 // FIXME: this doesn't display
911 function output ($template, $title_fs) {
912 $selected = &$this->selected;
913 $current = &$this->current;
915 if ($selected && $selected->getVersion() != $current->getVersion()) {
917 $pagelink = WikiLink($selected);
921 $pagelink = WikiLink($this->page);
924 $title = new FormattedText ($title_fs, $pagelink);
925 $template = Template($template, $this->tokens);
927 //GeneratePage($template, $title, $rev);
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.",
935 HTML::p(_("Please check it through before saving."))));
941 $Log: not supported by cvs2svn $
942 Revision 1.89 2005/01/21 14:07:49 rurban
945 Revision 1.88 2004/12/17 16:39:03 rurban
948 Revision 1.87 2004/12/16 18:28:05 rurban
949 keep wikiblog summary = page title
951 Revision 1.86 2004/12/11 14:50:15 rurban
952 new edit_convert button, to get rid of old markup eventually
954 Revision 1.85 2004/12/06 19:49:56 rurban
955 enable action=remove which is undoable and seeable in RecentChanges: ADODB ony for now.
956 renamed delete_page to purge_page.
957 enable action=edit&version=-1 to force creation of a new version.
958 added BABYCART_PATH config
959 fixed magiqc in adodb.inc.php
962 Revision 1.84 2004/12/04 12:58:26 rurban
963 enable babycart Blog::SpamAssassin module on ENABLE_SPAMASSASSIN=true
964 (currently only for php >= 4.3.0)
966 Revision 1.83 2004/12/04 11:55:39 rurban
967 First simple AntiSpam prevention:
968 No more than 20 new http:// links allowed
970 Revision 1.82 2004/11/30 22:21:56 rurban
971 changed gif to optimized (pngout) png
973 Revision 1.81 2004/11/29 17:57:27 rurban
974 translated pulldown buttons
976 Revision 1.80 2004/11/25 17:20:51 rurban
977 and again a couple of more native db args: backlinks
979 Revision 1.79 2004/11/21 11:59:20 rurban
980 remove final \n to be ob_cache independent
982 Revision 1.78 2004/11/16 17:57:45 rurban
983 fix search&replace button
984 use new addTagButton machinery
985 new showPulldown for categories, TODO: in a seperate request
987 Revision 1.77 2004/11/15 15:52:35 rurban
990 Revision 1.76 2004/11/15 15:37:34 rurban
992 don't use document.write for replace, otherwise self.opener is not defined.
994 Revision 1.75 2004/09/16 08:00:52 rurban
997 Revision 1.74 2004/07/03 07:36:28 rurban
998 do not get unneccessary content
1000 Revision 1.73 2004/06/16 21:23:44 rurban
1001 fixed non-object fatal #215
1003 Revision 1.72 2004/06/14 11:31:37 rurban
1004 renamed global $Theme to $WikiTheme (gforge nameclash)
1005 inherit PageList default options from PageList
1006 default sortby=pagename
1007 use options in PageList_Selectable (limit, sortby, ...)
1008 added action revert, with button at action=diff
1009 added option regex to WikiAdminSearchReplace
1011 Revision 1.71 2004/06/03 18:06:29 rurban
1012 fix file locking issues (only needed on write)
1013 fixed immediate LANG and THEME in-session updates if not stored in prefs
1014 advanced editpage toolbars (search & replace broken)
1016 Revision 1.70 2004/06/02 20:47:47 rurban
1017 dont use the wikiaction class
1019 Revision 1.69 2004/06/02 10:17:56 rurban
1020 integrated search/replace into toolbar
1021 added save+preview buttons
1023 Revision 1.68 2004/06/01 15:28:00 rurban
1024 AdminUser only ADMIN_USER not member of Administrators
1025 some RateIt improvements by dfrankow
1026 edit_toolbar buttons
1028 Revision _1.6 2004/05/26 15:48:00 syilek
1029 fixed problem with creating page with slashes from one true page
1031 Revision _1.5 2004/05/25 16:51:53 syilek
1032 added ability to create a page from the category page and not have to edit it
1034 Revision 1.67 2004/05/27 17:49:06 rurban
1035 renamed DB_Session to DbSession (in CVS also)
1036 added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
1037 remove leading slash in error message
1038 added force_unlock parameter to File_Passwd (no return on stale locks)
1039 fixed adodb session AffectedRows
1040 added FileFinder helpers to unify local filenames and DATA_PATH names
1041 editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
1043 Revision 1.66 2004/04/29 23:25:12 rurban
1044 re-ordered locale init (as in 1.3.9)
1045 fixed loadfile with subpages, and merge/restore anyway
1046 (sf.net bug #844188)
1048 Revision 1.65 2004/04/18 01:11:52 rurban
1049 more numeric pagename fixes.
1050 fixed action=upload with merge conflict warnings.
1051 charset changed from constant to global (dynamic utf-8 switching)
1053 Revision 1.64 2004/04/06 19:48:56 rurban
1054 temp workaround for action=edit AddComment form
1056 Revision 1.63 2004/03/24 19:39:02 rurban
1057 php5 workaround code (plus some interim debugging code in XmlElement)
1058 php5 doesn't work yet with the current XmlElement class constructors,
1059 WikiUserNew does work better than php4.
1060 rewrote WikiUserNew user upgrading to ease php5 update
1061 fixed pref handling in WikiUserNew
1062 added Email Notification
1063 added simple Email verification
1064 removed emailVerify userpref subclass: just a email property
1065 changed pref binary storage layout: numarray => hash of non default values
1066 print optimize message only if really done.
1067 forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string.
1068 prefs should be stored in db or homepage, besides the current session.
1070 Revision 1.62 2004/03/17 18:41:05 rurban
1071 initial_content and template support for CreatePage
1073 Revision 1.61 2004/03/12 20:59:17 rurban
1074 important cookie fix by Konstantin Zadorozhny
1075 new editpage feature: JS_SEARCHREPLACE
1077 Revision 1.60 2004/02/15 21:34:37 rurban
1078 PageList enhanced and improved.
1079 fixed new WikiAdmin... plugins
1080 editpage, Theme with exp. htmlarea framework
1081 (htmlarea yet committed, this is really questionable)
1082 WikiUser... code with better session handling for prefs
1083 enhanced UserPreferences (again)
1084 RecentChanges for show_deleted: how should pages be deleted then?
1086 Revision 1.59 2003/12/07 20:35:26 carstenklapp
1087 Bugfix: Concurrent updates broken since after 1.3.4 release: Fatal
1088 error: Call to undefined function: gettransformedcontent() in
1089 /home/groups/p/ph/phpwiki/htdocs/phpwiki2/lib/editpage.php on line
1092 Revision 1.58 2003/03/10 18:25:22 dairiki
1093 Bug/typo fix. If you use the edit page to un/lock a page, it
1094 failed with: Fatal error: Call to a member function on a
1095 non-object in editpage.php on line 136
1097 Revision 1.57 2003/02/26 03:40:22 dairiki
1098 New action=create. Essentially the same as action=edit, except that if the
1099 page already exists, it falls back to action=browse.
1101 This is for use in the "question mark" links for unknown wiki words
1102 to avoid problems and confusion when following links from stale pages.
1103 (If the "unknown page" has been created in the interim, the user probably
1104 wants to view the page before editing it.)
1106 Revision 1.56 2003/02/21 18:07:14 dairiki
1107 Minor, nitpicky, currently inconsequential changes.
1109 Revision 1.55 2003/02/21 04:10:58 dairiki
1110 Fixes for new cached markup.
1111 Some minor code cleanups.
1113 Revision 1.54 2003/02/16 19:47:16 dairiki
1114 Update WikiDB timestamp when editing or deleting pages.
1116 Revision 1.53 2003/02/15 23:20:27 dairiki
1117 Redirect back to browse current version of page upon save,
1118 even when no changes were made.
1120 Revision 1.52 2003/01/03 22:22:00 carstenklapp
1121 Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
1123 Revision 1.51 2003/01/03 02:43:26 carstenklapp
1124 New class LoadFileConflictPageEditor, for merging / comparing a loaded
1125 pgsrc file with an existing page.
1132 // c-basic-offset: 4
1133 // c-hanging-comment-ender-p: nil
1134 // indent-tabs-mode: nil