2 require_once 'lib/Template.php';
6 function PageEditor(&$request)
8 $this->request = &$request;
10 $this->user = $request->getUser();
11 $this->page = $request->getPage();
13 $this->current = $this->page->getCurrentRevision(false);
15 // HACKish short circuit to browse on action=create
16 if ($request->getArg('action') == 'create') {
17 if (!$this->current->hasDefaultContents())
18 $request->redirect(WikiURL($this->page->getName())); // noreturn
21 $this->meta = array('author' => $this->user->getId(),
22 'author_id' => $this->user->getAuthenticatedId(),
25 $this->tokens = array();
28 $backend = WYSIWYG_BACKEND;
29 // TODO: error message
30 require_once("lib/WysiwygEdit/$backend.php");
31 $class = "WysiwygEdit_$backend";
32 $this->WysiwygEdit = new $class();
35 require_once 'lib/Captcha.php';
36 $this->Captcha = new Captcha($this->meta);
39 $version = $request->getArg('version');
40 if ($version !== false) {
41 $this->selected = $this->page->getRevision($version);
42 $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;
51 $this->_initializeState();
52 $this->_initialEdit = true;
54 // The edit request has specified some initial content from a template
55 if (($template = $request->getArg('template'))
56 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']);
74 $tokens = &$this->tokens;
75 $tokens['PAGE_LOCKED_MESSAGE'] = '';
76 $tokens['LOCK_CHANGED_MSG'] = '';
77 $tokens['CONCURRENT_UPDATE_MESSAGE'] = '';
80 if (isset($r->args['pref']['editWidth'])
81 and ($r->getPref('editWidth') != $r->args['pref']['editWidth'])
83 $r->_prefs->set('editWidth', $r->args['pref']['editWidth']);
85 if (isset($r->args['pref']['editHeight'])
86 and ($r->getPref('editHeight') != $r->args['pref']['editHeight'])
88 $r->_prefs->set('editHeight', $r->args['pref']['editHeight']);
91 if ($this->isModerated())
92 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getModeratedMessage();
94 if (!$this->canEdit()) {
95 if ($this->isInitialEdit())
96 return $this->viewSource();
97 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
98 } elseif ($r->getArg('save_and_redirect_to') != "") {
99 if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
100 $this->tokens['PAGE_LOCKED_MESSAGE'] =
101 HTML::p(HTML::h1($this->Captcha->failed_msg));
102 } elseif ($this->savePage()) {
104 $request->setArg('action', false);
105 $r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
106 return true; // Page saved.
109 } elseif ($this->editaction == 'save') {
110 if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
111 $this->tokens['PAGE_LOCKED_MESSAGE'] =
112 HTML::p(HTML::h1($this->Captcha->failed_msg));
113 } elseif ($this->savePage()) {
114 return true; // Page saved.
118 } // coming from loadfile conflicts
119 elseif ($this->editaction == 'keep_old') {
120 // keep old page and do nothing
121 $this->_redirectToBrowsePage();
122 //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
124 } elseif ($this->editaction == 'overwrite') {
125 // take the new content without diff
126 $source = $this->request->getArg('loadfile');
127 require_once 'lib/loadsave.php';
128 $this->request->setArg('loadfile', 1);
129 $this->request->setArg('overwrite', 1);
130 $this->request->setArg('merge', 0);
131 LoadFileOrDir($this->request);
132 $this->_redirectToBrowsePage();
133 //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
135 } elseif ($this->editaction == 'upload') {
137 $plugin = WikiPluginLoader("UpLoad");
139 // add link to content
143 if ($saveFailed and $this->isConcurrentUpdate()) {
144 // Get the text of the original page, and the two conflicting edits
145 // The diff3 class takes arrays as input. So retrieve content as
146 // an array, or convert it as necesary.
147 $orig = $this->page->getRevision($this->_currentVersion);
148 // FIXME: what if _currentVersion has be deleted?
149 $orig_content = $orig->getContent();
150 $this_content = explode("\n", $this->_content);
151 $other_content = $this->current->getContent();
152 require_once 'lib/diff3.php';
153 $diff = new diff3($orig_content, $this_content, $other_content);
154 $output = $diff->merged_output(_("Your version"), _("Other version"));
155 // Set the content of the textarea to the merged diff
156 // output, and update the version
157 $this->_content = implode("\n", $output);
158 $this->_currentVersion = $this->current->getVersion();
159 $this->version = $this->_currentVersion;
160 $unresolved = $diff->ConflictingBlocks;
161 $tokens['CONCURRENT_UPDATE_MESSAGE']
162 = $this->getConflictMessage($unresolved);
163 } elseif ($saveFailed && !$this->_isSpam) {
164 $tokens['CONCURRENT_UPDATE_MESSAGE'] =
165 HTML(HTML::h2(_("Some internal editing error")),
166 HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
167 HTML::p(HTML::em(_("&version=-1 might help."))));
170 if ($this->editaction == 'edit_convert')
171 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
172 if ($this->editaction == 'preview')
173 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
174 if ($this->editaction == 'diff')
175 $tokens['PREVIEW_CONTENT'] = $this->getDiff();
177 // FIXME: NOT_CURRENT_MESSAGE?
178 $tokens = array_merge($tokens, $this->getFormElements());
180 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
181 require_once 'lib/EditToolbar.php';
182 $toolbar = new EditToolbar();
183 $tokens = array_merge($tokens, $toolbar->getTokens());
186 return $this->output('editpage', _("Edit: %s"));
189 function output($template, $title_fs)
192 $selected = &$this->selected;
193 $current = &$this->current;
195 if ($selected && $selected->getVersion() != $current->getVersion()) {
197 $pagelink = WikiLink($selected);
200 $pagelink = WikiLink($this->page);
203 $title = new FormattedText ($title_fs, $pagelink);
204 // not for dumphtml or viewsource
205 if (ENABLE_WYSIWYG and $template == 'editpage') {
206 $WikiTheme->addMoreHeaders($this->WysiwygEdit->Head());
207 //$tokens['PAGE_SOURCE'] = $this->WysiwygEdit->ConvertBefore($this->_content);
209 $template = Template($template, $this->tokens);
210 /* Tell google (and others) not to take notice of edit links */
211 if (GOOGLE_LINKS_NOFOLLOW)
212 $args = array('ROBOTS_META' => "noindex,nofollow");
213 GeneratePage($template, $title, $rev);
217 function viewSource()
219 assert($this->isInitialEdit());
220 assert($this->selected);
222 $this->tokens['PAGE_SOURCE'] = $this->_content;
223 $this->tokens['HIDDEN_INPUTS'] = HiddenInputs($this->request->getArgs());
224 return $this->output('viewsource', _("View Source: %s"));
227 function updateLock()
230 if (!ENABLE_PAGE_PUBLIC && !ENABLE_EXTERNAL_PAGES) {
231 if ((bool)$this->page->get('locked') == (bool)$this->locked)
232 return false; // Not changed.
235 if (!$this->user->isAdmin()) {
236 // FIXME: some sort of message
237 return false; // not allowed.
239 if ((bool)$this->page->get('locked') != (bool)$this->locked) {
240 $this->page->set('locked', (bool)$this->locked);
241 $this->tokens['LOCK_CHANGED_MSG']
243 ? _("Page now locked.")
244 : _("Page now unlocked.") . " ");
247 if (ENABLE_PAGE_PUBLIC and (bool)$this->page->get('public') != (bool)$this->public) {
248 $this->page->set('public', (bool)$this->public);
249 $this->tokens['LOCK_CHANGED_MSG']
251 ? _("Page now public.")
252 : _("Page now not-public."));
256 if (ENABLE_EXTERNAL_PAGES) {
257 if ((bool)$this->page->get('external') != (bool)$this->external) {
258 $this->page->set('external', (bool)$this->external);
259 $this->tokens['LOCK_CHANGED_MSG']
261 ? _("Page now external.")
262 : _("Page now not-external.")) . " ";
266 return $changed; // lock changed.
271 $request = &$this->request;
273 if ($this->isUnchanged()) {
274 // Allow admin lock/unlock even if
275 // no text changes were made.
276 if ($this->updateLock()) {
277 $dbi = $request->getDbh();
280 // Save failed. No changes made.
281 $this->_redirectToBrowsePage();
282 // user will probably not see the rest of this...
283 require_once 'lib/display.php';
284 // force browse of current version:
285 $request->setArg('action', false);
286 $request->setArg('version', false);
287 displayPage($request, 'nochanges');
291 if (!$this->user->isAdmin() and $this->isSpam()) {
292 $this->_isSpam = true;
295 // Save failed. No changes made.
296 $this->_redirectToBrowsePage();
297 // user will probably not see the rest of this...
298 require_once 'lib/display.php';
299 // force browse of current version:
300 $request->setArg('version', false);
301 displayPage($request, 'nochanges');
306 $page = &$this->page;
308 // Include any meta-data from original page version which
309 // has not been explicitly updated.
310 // (Except don't propagate pgsrc_version --- moot for now,
311 // because at present it never gets into the db...)
312 $meta = $this->selected->getMetaData();
313 unset($meta['pgsrc_version']);
314 $meta = array_merge($meta, $this->meta);
317 $this->_content = $this->getContent();
318 $newrevision = $page->save($this->_content,
321 : $this->_currentVersion + 1,
324 if (!isa($newrevision, 'WikiDB_PageRevision')) {
325 // Save failed. (Concurrent updates).
329 // New contents successfully saved...
332 // Clean out archived versions of this page.
333 require_once 'lib/ArchiveCleaner.php';
334 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
335 $cleaner->cleanPageRevisions($page);
337 /* generate notification emails done in WikiDB::save to catch
338 all direct calls (admin plugins) */
340 // look at the errorstack
341 $errors = $GLOBALS['ErrorManager']->_postponed_errors;
342 $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
343 $GLOBALS['ErrorManager']->_postponed_errors = $errors;
345 $dbi = $request->getDbh();
349 if (empty($warnings->_content) && !$WikiTheme->getImageURL('signature')) {
350 // Do redirect to browse page if no signature has
351 // been defined. In this case, the user will most
352 // likely not see the rest of the HTML we generate
354 $request->setArg('action', false);
355 $this->_redirectToBrowsePage();
358 // Force browse of current page version.
359 $request->setArg('version', false);
360 // testme: does preview and more need action=edit?
361 $request->setArg('action', false);
363 $template = Template('savepage', $this->tokens);
364 $template->replace('CONTENT', $newrevision->getTransformedContent());
365 if (!empty($warnings->_content)) {
366 $template->replace('WARNINGS', $warnings);
367 unset($GLOBALS['ErrorManager']->_postponed_errors);
370 $pagelink = WikiLink($page);
372 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
376 function isConcurrentUpdate()
378 assert($this->current->getVersion() >= $this->_currentVersion);
379 return $this->current->getVersion() != $this->_currentVersion;
384 return !$this->page->get('locked') || $this->user->isAdmin();
387 function isInitialEdit()
389 return $this->_initialEdit;
392 function isUnchanged()
394 $current = &$this->current;
396 if ($this->meta['markup'] != $current->get('markup'))
399 return $this->_content == $current->getPackedContent();
403 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
404 * Need to check dynamically some blacklist wikipage settings
405 * (plugin WikiAccessRestrictions) and some static blacklist.
407 * More than NUM_SPAM_LINKS (default: 20) new external links.
408 * Disabled if NUM_SPAM_LINKS is 0
409 * ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
410 * ENABLE_SPAMBLOCKLIST: content domain blacklist
414 $current = &$this->current;
415 $request = &$this->request;
417 $oldtext = $current->getPackedContent();
418 $newtext =& $this->_content;
419 $numlinks = $this->numLinks($newtext);
420 $newlinks = $numlinks - $this->numLinks($oldtext);
421 // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
422 // better use a certain text : link ratio.
424 // 1. Not more than NUM_SPAM_LINKS (default: 20) new external links
425 if ((NUM_SPAM_LINKS > 0) and ($newlinks >= NUM_SPAM_LINKS)) {
426 // Allow strictly authenticated users?
427 // TODO: mail the admin?
428 $this->tokens['PAGE_LOCKED_MESSAGE'] =
429 HTML($this->getSpamMessage(),
430 HTML::p(HTML::strong(_("Too many external links."))));
433 // 2. external babycart (SpamAssassin) check
434 // This will probably prevent from discussing sex or viagra related topics. So beware.
435 if (ENABLE_SPAMASSASSIN) {
436 require_once 'lib/spam_babycart.php';
437 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
438 $this->user->getId())
440 // TODO: mail the admin
441 if (is_array($babycart))
442 $this->tokens['PAGE_LOCKED_MESSAGE'] =
443 HTML($this->getSpamMessage(),
444 HTML::p(HTML::em(_("SpamAssassin reports: "),
445 join("\n", $babycart))));
449 // 3. extract (new) links and check surbl for blocked domains
450 if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
451 require_once 'lib/SpamBlocklist.php';
452 require_once 'lib/InlineParser.php';
453 $parsed = TransformLinks($newtext);
454 $oldparsed = TransformLinks($oldtext);
456 foreach ($oldparsed->_content as $link) {
457 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
458 $uri = $link->_getURL($this->page->getName());
463 foreach ($parsed->_content as $link) {
464 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
465 $uri = $link->_getURL($this->page->getName());
466 // only check new links, so admins may add blocked links.
467 if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
468 // TODO: mail the admin
469 $this->tokens['PAGE_LOCKED_MESSAGE'] =
470 HTML($this->getSpamMessage(),
471 HTML::p(HTML::strong(_("External links contain blocked domains:")),
472 HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
473 $uri . " [" . $res[2] . "]", $res[0], $res[1])))));
486 /** Number of external links in the wikitext
488 function numLinks(&$text)
490 return substr_count($text, "http://") + substr_count($text, "https://");
493 /** Header of the Anti Spam message
495 function getSpamMessage()
498 HTML(HTML::h2(_("Spam Prevention")),
499 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
501 _("Sorry for the inconvenience.")),
505 function getPreview()
507 require_once 'lib/PageType.php';
508 $this->_content = $this->getContent();
509 return new TransformedText($this->page, $this->_content, $this->meta);
512 function getConvertedPreview()
514 require_once 'lib/PageType.php';
515 $this->_content = $this->getContent();
516 $this->meta['markup'] = 2.0;
517 $this->_content = ConvertOldMarkup($this->_content);
518 return new TransformedText($this->page, $this->_content, $this->meta);
523 require_once 'lib/diff.php';
526 $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
527 if ($diff->isEmpty()) {
528 $html->pushContent(HTML::hr(),
529 HTML::p('[', _("Versions are identical"),
532 // New CSS formatted unified diffs
533 $fmt = new HtmlUnifiedDiffFormatter;
534 $html->pushContent($fmt->format($diff));
539 // possibly convert HTMLAREA content back to Wiki markup
540 function getContent()
542 if (ENABLE_WYSIWYG) {
543 // don't store everything as html
544 if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
545 // Wikiwyg shortcut to avoid the InlineTransformer:
546 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
547 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
548 $this->_content = join("", $xml_output->_content);
550 $this->meta['pagetype'] = 'html';
552 return $this->_content;
554 return $this->_content;
558 function getLockedMessage()
561 HTML(HTML::h2(_("Page Locked")),
562 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
563 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
564 HTML::p(_("Sorry for the inconvenience.")));
567 function isModerated()
569 return $this->page->get('moderation');
572 function getModeratedMessage()
575 HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
576 HTML::p(fmt("You can edit away, but your changes will have to be approved by the defined moderators at the definition in %s", WikiLink(_("ModeratedPage")))),
577 HTML::p(fmt("The approval has a grace period of 5 days. If you have your E-Mail defined in your %s, you will get a notification of approval or rejection.",
578 WikiLink(_("UserPreferences")))));
581 function getConflictMessage($unresolved = false)
584 xgettext only knows about c/c++ line-continuation strings
585 it does not know about php's dot operator.
586 We want to translate this entire paragraph as one string, of course.
589 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
592 $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.",
593 "<<<<<<< " . _("Your version"),
594 ">>>>>>> " . _("Other version")));
596 $message = HTML::p(_("Please check it through before saving."));
598 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
599 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
601 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
602 HTML::li(_("Save your updated changes.")));
605 HTML(HTML::h2(_("Conflicting Edits!")),
606 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
607 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.")),
611 function getTextArea()
613 $request = &$this->request;
615 $readonly = !$this->canEdit(); // || $this->isConcurrentUpdate();
617 // WYSIWYG will need two pagetypes: raw wikitest and converted html
618 if (ENABLE_WYSIWYG) {
619 $this->_wikicontent = $this->_content;
620 $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
621 // $this->getPreview();
622 //$this->_htmlcontent = $this->_content->asXML();
625 $textarea = HTML::textarea(array('class' => 'wikiedit',
626 'name' => 'edit[content]',
627 'id' => 'edit-content',
628 'rows' => $request->getPref('editHeight'),
629 'cols' => $request->getPref('editWidth'),
630 'readonly' => (bool)$readonly),
632 if (ENABLE_WYSIWYG) {
633 return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
634 $textarea->getAttr('name'));
639 function getFormElements()
642 $request = &$this->request;
643 $page = &$this->page;
645 $h = array('action' => 'edit',
646 'pagename' => $page->getName(),
647 'version' => $this->version,
648 'edit[pagetype]' => $this->meta['pagetype'],
649 'edit[current_version]' => $this->_currentVersion);
651 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
652 $el['EDIT_TEXTAREA'] = $this->getTextArea();
653 if (ENABLE_CAPTCHA) {
654 $el = array_merge($el, $this->Captcha->getFormElements());
657 = HTML::input(array('type' => 'text',
658 'class' => 'wikitext',
659 'id' => 'edit-summary',
660 'name' => 'edit[summary]',
663 'value' => $this->meta['summary']));
665 = HTML::input(array('type' => 'checkbox',
666 'name' => 'edit[minor_edit]',
667 'id' => 'edit-minor_edit',
668 'checked' => (bool)$this->meta['is_minor_edit']));
670 = HTML::input(array('type' => 'checkbox',
671 'name' => 'edit[markup]',
673 'checked' => $this->meta['markup'] < 2.0,
674 'id' => 'useOldMarkup',
675 'onclick' => 'showOldMarkupRules(this.checked)'));
676 $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0)
677 ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
679 = HTML::input(array('type' => 'checkbox',
680 'name' => 'edit[locked]',
681 'id' => 'edit-locked',
682 'disabled' => (bool)!$this->user->isAdmin(),
683 'checked' => (bool)$this->locked));
684 if (ENABLE_PAGE_PUBLIC) {
686 = HTML::input(array('type' => 'checkbox',
687 'name' => 'edit[public]',
688 'id' => 'edit-public',
689 'disabled' => (bool)!$this->user->isAdmin(),
690 'checked' => (bool)$this->page->get('public')));
692 if (ENABLE_EXTERNAL_PAGES) {
694 = HTML::input(array('type' => 'checkbox',
695 'name' => 'edit[external]',
696 'id' => 'edit-external',
697 'disabled' => (bool)!$this->user->isAdmin(),
698 'checked' => (bool)$this->page->get('external')));
700 if (ENABLE_WYSIWYG) {
701 if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
702 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
706 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
708 array('accesskey' => 'p',
709 'title' => 'Preview the current content [alt-p]'));
711 //if (!$this->isConcurrentUpdate() && $this->canEdit())
712 $el['SAVE_B'] = Button('submit:edit[save]',
713 _("Save"), 'wikiaction',
714 array('accesskey' => 's',
715 'title' => 'Save the current content as wikipage [alt-s]'));
716 $el['CHANGES_B'] = Button('submit:edit[diff]',
717 _("Changes"), 'wikiaction',
718 array('accesskey' => 'c',
719 'title' => 'Preview the current changes as diff [alt-c]'));
720 $el['UPLOAD_B'] = Button('submit:edit[upload]',
721 _("Upload"), 'wikiaction',
722 array('title' => 'Select a local file and press Upload to attach into this page'));
723 $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
724 _("Spell Check"), 'wikiaction',
725 array('title' => 'Check the spelling'));
726 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
729 = HTML::input(array('type' => 'text',
732 'class' => "numeric",
733 'name' => 'pref[editWidth]',
734 'id' => 'pref-editWidth',
735 'value' => $request->getPref('editWidth'),
736 'onchange' => 'this.form.submit();'));
738 = HTML::input(array('type' => 'text',
741 'class' => "numeric",
742 'name' => 'pref[editHeight]',
743 'id' => 'pref-editHeight',
744 'value' => $request->getPref('editHeight'),
745 'onchange' => 'this.form.submit();'));
746 $el['SEP'] = $WikiTheme->getButtonSeparator();
747 $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
748 HTML::em($this->user->getId()));
753 function _redirectToBrowsePage()
755 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
758 function _restoreState()
760 $request = &$this->request;
762 $posted = $request->getArg('edit');
763 $request->setArg('edit', false);
766 || !$request->isPost()
767 || !in_array($request->getArg('action'), array('edit', 'loadfile'))
771 if (!isset($posted['content']) || !is_string($posted['content']))
773 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
774 rtrim($posted['content']));
775 $this->_content = $this->getContent();
777 $this->_currentVersion = (int)$posted['current_version'];
779 if ($this->_currentVersion < 0)
781 if ($this->_currentVersion > $this->current->getVersion())
782 return false; // FIXME: some kind of warning?
784 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
785 $meta['markup'] = $is_old_markup ? false : 2.0;
786 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
787 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
788 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
790 $meta['captcha_input'] = !empty($posted['captcha_input']) ?
791 $posted['captcha_input'] : '';
793 $this->meta = array_merge($this->meta, $meta);
794 $this->locked = !empty($posted['locked']);
795 if (ENABLE_PAGE_PUBLIC)
796 $this->public = !empty($posted['public']);
797 if (ENABLE_EXTERNAL_PAGES)
798 $this->external = !empty($posted['external']);
800 foreach (array('preview', 'save', 'edit_convert',
801 'keep_old', 'overwrite', 'diff', 'upload') as $o) {
802 if (!empty($posted[$o]))
803 $this->editaction = $o;
805 if (empty($this->editaction))
806 $this->editaction = 'edit';
811 function _initializeState()
813 $request = &$this->request;
814 $current = &$this->current;
815 $selected = &$this->selected;
816 $user = &$this->user;
819 NoSuchRevision($request, $this->page, $this->version); // noreturn
821 $this->_currentVersion = $current->getVersion();
822 $this->_content = $selected->getPackedContent();
824 $this->locked = $this->page->get('locked');
826 // If author same as previous author, default minor_edit to on.
827 $age = $this->meta['mtime'] - $current->get('mtime');
828 $this->meta['is_minor_edit'] = ($age < MINOR_EDIT_TIMEOUT
829 && $current->get('author') == $user->getId()
832 // Default for new pages is new-style markup.
833 if ($selected->hasDefaultContents())
834 $is_new_markup = true;
836 $is_new_markup = $selected->get('markup') >= 2.0;
838 $this->meta['markup'] = $is_new_markup ? 2.0 : false;
839 $this->meta['pagetype'] = $selected->get('pagetype');
840 if ($this->meta['pagetype'] == 'wikiblog')
841 $this->meta['summary'] = $selected->get('summary'); // keep blog title
843 $this->meta['summary'] = '';
844 $this->editaction = 'edit';
848 class LoadFileConflictPageEditor
851 function editPage($saveFailed = true)
853 $tokens = &$this->tokens;
855 if (!$this->canEdit()) {
856 if ($this->isInitialEdit()) {
857 return $this->viewSource();
859 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
860 } elseif ($this->editaction == 'save') {
861 if ($this->savePage()) {
862 return true; // Page saved.
867 if ($saveFailed || $this->isConcurrentUpdate()) {
868 // Get the text of the original page, and the two conflicting edits
869 // The diff class takes arrays as input. So retrieve content as
870 // an array, or convert it as necesary.
871 $orig = $this->page->getRevision($this->_currentVersion);
872 $this_content = explode("\n", $this->_content);
873 $other_content = $this->current->getContent();
874 require_once 'lib/diff.php';
875 $diff2 = new Diff($other_content, $this_content);
876 $context_lines = max(4, count($other_content) + 1,
877 count($this_content) + 1);
878 $fmt = new BlockDiffFormatter($context_lines);
880 $this->_content = $fmt->format($diff2);
881 // FIXME: integrate this into class BlockDiffFormatter
882 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
884 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
887 $this->_currentVersion = $this->current->getVersion();
888 $this->version = $this->_currentVersion;
889 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
892 if ($this->editaction == 'edit_convert')
893 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
894 if ($this->editaction == 'preview')
895 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
897 // FIXME: NOT_CURRENT_MESSAGE?
898 $tokens = array_merge($tokens, $this->getFormElements());
899 // we need all GET params for loadfile overwrite
900 if ($this->request->getArg('action') == 'loadfile') {
902 $this->tokens['HIDDEN_INPUTS'] =
904 (array('source' => $this->request->getArg('source'),
906 $this->tokens['HIDDEN_INPUTS']);
907 // add two conflict resolution buttons before preview and save.
908 $tokens['PREVIEW_B'] = HTML(
909 Button('submit:edit[keep_old]',
910 _("Keep old"), 'wikiaction'),
912 Button('submit:edit[overwrite]',
913 _("Overwrite with new"), 'wikiaction'),
915 $tokens['PREVIEW_B']);
917 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
918 include_once 'lib/EditToolbar.php';
919 $toolbar = new EditToolbar();
920 $tokens = array_merge($tokens, $toolbar->getTokens());
923 return $this->output('editpage', _("Merge and Edit: %s"));
926 function output($template, $title_fs)
928 $selected = &$this->selected;
929 $current = &$this->current;
931 if ($selected && $selected->getVersion() != $current->getVersion()) {
933 $pagelink = WikiLink($selected);
936 $pagelink = WikiLink($this->page);
939 $title = new FormattedText ($title_fs, $pagelink);
940 $this->tokens['HEADER'] = $title;
941 //hack! there's no TITLE in editpage, but in the previous top template
942 if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
943 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
945 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
946 $this->tokens['PAGE_LOCKED_MESSAGE']);
947 $template = Template($template, $this->tokens);
949 //GeneratePage($template, $title, $rev);
954 function getConflictMessage($unresolved = false)
956 $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.",
959 HTML::p(_("Please check it through before saving."))));
968 // c-hanging-comment-ender-p: nil
969 // indent-tabs-mode: nil