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 $meta = $this->selected->getMetaData();
311 $meta = array_merge($meta, $this->meta);
314 $this->_content = $this->getContent();
315 $newrevision = $page->save($this->_content,
318 : $this->_currentVersion + 1,
321 if (!isa($newrevision, 'WikiDB_PageRevision')) {
322 // Save failed. (Concurrent updates).
326 // New contents successfully saved...
329 // Clean out archived versions of this page.
330 require_once 'lib/ArchiveCleaner.php';
331 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
332 $cleaner->cleanPageRevisions($page);
334 /* generate notification emails done in WikiDB::save to catch
335 all direct calls (admin plugins) */
337 // look at the errorstack
338 $errors = $GLOBALS['ErrorManager']->_postponed_errors;
339 $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
340 $GLOBALS['ErrorManager']->_postponed_errors = $errors;
342 $dbi = $request->getDbh();
346 if (empty($warnings->_content) && !$WikiTheme->getImageURL('signature')) {
347 // Do redirect to browse page if no signature has
348 // been defined. In this case, the user will most
349 // likely not see the rest of the HTML we generate
351 $request->setArg('action', false);
352 $this->_redirectToBrowsePage();
355 // Force browse of current page version.
356 $request->setArg('version', false);
357 // testme: does preview and more need action=edit?
358 $request->setArg('action', false);
360 $template = Template('savepage', $this->tokens);
361 $template->replace('CONTENT', $newrevision->getTransformedContent());
362 if (!empty($warnings->_content)) {
363 $template->replace('WARNINGS', $warnings);
364 unset($GLOBALS['ErrorManager']->_postponed_errors);
367 $pagelink = WikiLink($page);
369 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
373 function isConcurrentUpdate()
375 assert($this->current->getVersion() >= $this->_currentVersion);
376 return $this->current->getVersion() != $this->_currentVersion;
381 return !$this->page->get('locked') || $this->user->isAdmin();
384 function isInitialEdit()
386 return $this->_initialEdit;
389 function isUnchanged()
391 $current = &$this->current;
393 if ($this->meta['markup'] != $current->get('markup'))
396 return $this->_content == $current->getPackedContent();
400 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
401 * Need to check dynamically some blacklist wikipage settings
402 * (plugin WikiAccessRestrictions) and some static blacklist.
404 * More than NUM_SPAM_LINKS (default: 20) new external links.
405 * Disabled if NUM_SPAM_LINKS is 0
406 * ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
407 * ENABLE_SPAMBLOCKLIST: content domain blacklist
411 $current = &$this->current;
412 $request = &$this->request;
414 $oldtext = $current->getPackedContent();
415 $newtext =& $this->_content;
416 $numlinks = $this->numLinks($newtext);
417 $newlinks = $numlinks - $this->numLinks($oldtext);
418 // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
419 // better use a certain text : link ratio.
421 // 1. Not more than NUM_SPAM_LINKS (default: 20) new external links
422 if ((NUM_SPAM_LINKS > 0) and ($newlinks >= NUM_SPAM_LINKS)) {
423 // Allow strictly authenticated users?
424 // TODO: mail the admin?
425 $this->tokens['PAGE_LOCKED_MESSAGE'] =
426 HTML($this->getSpamMessage(),
427 HTML::p(HTML::strong(_("Too many external links."))));
430 // 2. external babycart (SpamAssassin) check
431 // This will probably prevent from discussing sex or viagra related topics. So beware.
432 if (ENABLE_SPAMASSASSIN) {
433 require_once 'lib/spam_babycart.php';
434 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
435 $this->user->getId())
437 // TODO: mail the admin
438 if (is_array($babycart))
439 $this->tokens['PAGE_LOCKED_MESSAGE'] =
440 HTML($this->getSpamMessage(),
441 HTML::p(HTML::em(_("SpamAssassin reports: "),
442 join("\n", $babycart))));
446 // 3. extract (new) links and check surbl for blocked domains
447 if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
448 require_once 'lib/SpamBlocklist.php';
449 require_once 'lib/InlineParser.php';
450 $parsed = TransformLinks($newtext);
451 $oldparsed = TransformLinks($oldtext);
453 foreach ($oldparsed->_content as $link) {
454 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
455 $uri = $link->_getURL($this->page->getName());
460 foreach ($parsed->_content as $link) {
461 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
462 $uri = $link->_getURL($this->page->getName());
463 // only check new links, so admins may add blocked links.
464 if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
465 // TODO: mail the admin
466 $this->tokens['PAGE_LOCKED_MESSAGE'] =
467 HTML($this->getSpamMessage(),
468 HTML::p(HTML::strong(_("External links contain blocked domains:")),
469 HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
470 $uri . " [" . $res[2] . "]", $res[0], $res[1])))));
483 /** Number of external links in the wikitext
485 function numLinks(&$text)
487 return substr_count($text, "http://") + substr_count($text, "https://");
490 /** Header of the Anti Spam message
492 function getSpamMessage()
495 HTML(HTML::h2(_("Spam Prevention")),
496 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
498 _("Sorry for the inconvenience.")),
502 function getPreview()
504 require_once 'lib/PageType.php';
505 $this->_content = $this->getContent();
506 return new TransformedText($this->page, $this->_content, $this->meta);
509 function getConvertedPreview()
511 require_once 'lib/PageType.php';
512 $this->_content = $this->getContent();
513 $this->meta['markup'] = 2.0;
514 $this->_content = ConvertOldMarkup($this->_content);
515 return new TransformedText($this->page, $this->_content, $this->meta);
520 require_once 'lib/diff.php';
523 $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
524 if ($diff->isEmpty()) {
525 $html->pushContent(HTML::hr(),
526 HTML::p('[', _("Versions are identical"),
529 // New CSS formatted unified diffs
530 $fmt = new HtmlUnifiedDiffFormatter;
531 $html->pushContent($fmt->format($diff));
536 // possibly convert HTMLAREA content back to Wiki markup
537 function getContent()
539 if (ENABLE_WYSIWYG) {
540 // don't store everything as html
541 if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
542 // Wikiwyg shortcut to avoid the InlineTransformer:
543 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
544 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
545 $this->_content = join("", $xml_output->_content);
547 $this->meta['pagetype'] = 'html';
549 return $this->_content;
551 return $this->_content;
555 function getLockedMessage()
558 HTML(HTML::h2(_("Page Locked")),
559 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
560 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
561 HTML::p(_("Sorry for the inconvenience.")));
564 function isModerated()
566 return $this->page->get('moderation');
569 function getModeratedMessage()
572 HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
573 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")))),
574 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.",
575 WikiLink(_("UserPreferences")))));
578 function getConflictMessage($unresolved = false)
581 xgettext only knows about c/c++ line-continuation strings
582 it does not know about php's dot operator.
583 We want to translate this entire paragraph as one string, of course.
586 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
589 $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.",
590 "<<<<<<< " . _("Your version"),
591 ">>>>>>> " . _("Other version")));
593 $message = HTML::p(_("Please check it through before saving."));
595 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
596 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
598 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
599 HTML::li(_("Save your updated changes.")));
602 HTML(HTML::h2(_("Conflicting Edits!")),
603 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
604 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.")),
608 function getTextArea()
610 $request = &$this->request;
612 $readonly = !$this->canEdit(); // || $this->isConcurrentUpdate();
614 // WYSIWYG will need two pagetypes: raw wikitest and converted html
615 if (ENABLE_WYSIWYG) {
616 $this->_wikicontent = $this->_content;
617 $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
618 // $this->getPreview();
619 //$this->_htmlcontent = $this->_content->asXML();
622 $textarea = HTML::textarea(array('class' => 'wikiedit',
623 'name' => 'edit[content]',
624 'id' => 'edit-content',
625 'rows' => $request->getPref('editHeight'),
626 'cols' => $request->getPref('editWidth'),
627 'readonly' => (bool)$readonly),
629 if (ENABLE_WYSIWYG) {
630 return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
631 $textarea->getAttr('name'));
636 function getFormElements()
639 $request = &$this->request;
640 $page = &$this->page;
642 $h = array('action' => 'edit',
643 'pagename' => $page->getName(),
644 'version' => $this->version,
645 'edit[pagetype]' => $this->meta['pagetype'],
646 'edit[current_version]' => $this->_currentVersion);
648 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
649 $el['EDIT_TEXTAREA'] = $this->getTextArea();
650 if (ENABLE_CAPTCHA) {
651 $el = array_merge($el, $this->Captcha->getFormElements());
654 = HTML::input(array('type' => 'text',
655 'class' => 'wikitext',
656 'id' => 'edit-summary',
657 'name' => 'edit[summary]',
660 'value' => $this->meta['summary']));
662 = HTML::input(array('type' => 'checkbox',
663 'name' => 'edit[minor_edit]',
664 'id' => 'edit-minor_edit',
665 'checked' => (bool)$this->meta['is_minor_edit']));
667 = HTML::input(array('type' => 'checkbox',
668 'name' => 'edit[markup]',
670 'checked' => $this->meta['markup'] < 2.0,
671 'id' => 'useOldMarkup',
672 'onclick' => 'showOldMarkupRules(this.checked)'));
673 $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0)
674 ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
676 = HTML::input(array('type' => 'checkbox',
677 'name' => 'edit[locked]',
678 'id' => 'edit-locked',
679 'disabled' => (bool)!$this->user->isAdmin(),
680 'checked' => (bool)$this->locked));
681 if (ENABLE_PAGE_PUBLIC) {
683 = HTML::input(array('type' => 'checkbox',
684 'name' => 'edit[public]',
685 'id' => 'edit-public',
686 'disabled' => (bool)!$this->user->isAdmin(),
687 'checked' => (bool)$this->page->get('public')));
689 if (ENABLE_EXTERNAL_PAGES) {
691 = HTML::input(array('type' => 'checkbox',
692 'name' => 'edit[external]',
693 'id' => 'edit-external',
694 'disabled' => (bool)!$this->user->isAdmin(),
695 'checked' => (bool)$this->page->get('external')));
697 if (ENABLE_WYSIWYG) {
698 if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
699 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
703 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
705 array('accesskey' => 'p',
706 'title' => 'Preview the current content [alt-p]'));
708 //if (!$this->isConcurrentUpdate() && $this->canEdit())
709 $el['SAVE_B'] = Button('submit:edit[save]',
710 _("Save"), 'wikiaction',
711 array('accesskey' => 's',
712 'title' => 'Save the current content as wikipage [alt-s]'));
713 $el['CHANGES_B'] = Button('submit:edit[diff]',
714 _("Changes"), 'wikiaction',
715 array('accesskey' => 'c',
716 'title' => 'Preview the current changes as diff [alt-c]'));
717 $el['UPLOAD_B'] = Button('submit:edit[upload]',
718 _("Upload"), 'wikiaction',
719 array('title' => 'Select a local file and press Upload to attach into this page'));
720 $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
721 _("Spell Check"), 'wikiaction',
722 array('title' => 'Check the spelling'));
723 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
726 = HTML::input(array('type' => 'text',
729 'class' => "numeric",
730 'name' => 'pref[editWidth]',
731 'id' => 'pref-editWidth',
732 'value' => $request->getPref('editWidth'),
733 'onchange' => 'this.form.submit();'));
735 = HTML::input(array('type' => 'text',
738 'class' => "numeric",
739 'name' => 'pref[editHeight]',
740 'id' => 'pref-editHeight',
741 'value' => $request->getPref('editHeight'),
742 'onchange' => 'this.form.submit();'));
743 $el['SEP'] = $WikiTheme->getButtonSeparator();
744 $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
745 HTML::em($this->user->getId()));
750 function _redirectToBrowsePage()
752 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
755 function _restoreState()
757 $request = &$this->request;
759 $posted = $request->getArg('edit');
760 $request->setArg('edit', false);
763 || !$request->isPost()
764 || !in_array($request->getArg('action'), array('edit', 'loadfile'))
768 if (!isset($posted['content']) || !is_string($posted['content']))
770 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
771 rtrim($posted['content']));
772 $this->_content = $this->getContent();
774 $this->_currentVersion = (int)$posted['current_version'];
776 if ($this->_currentVersion < 0)
778 if ($this->_currentVersion > $this->current->getVersion())
779 return false; // FIXME: some kind of warning?
781 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
782 $meta['markup'] = $is_old_markup ? false : 2.0;
783 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
784 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
785 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
787 $meta['captcha_input'] = !empty($posted['captcha_input']) ?
788 $posted['captcha_input'] : '';
790 $this->meta = array_merge($this->meta, $meta);
791 $this->locked = !empty($posted['locked']);
792 if (ENABLE_PAGE_PUBLIC)
793 $this->public = !empty($posted['public']);
794 if (ENABLE_EXTERNAL_PAGES)
795 $this->external = !empty($posted['external']);
797 foreach (array('preview', 'save', 'edit_convert',
798 'keep_old', 'overwrite', 'diff', 'upload') as $o) {
799 if (!empty($posted[$o]))
800 $this->editaction = $o;
802 if (empty($this->editaction))
803 $this->editaction = 'edit';
808 function _initializeState()
810 $request = &$this->request;
811 $current = &$this->current;
812 $selected = &$this->selected;
813 $user = &$this->user;
816 NoSuchRevision($request, $this->page, $this->version); // noreturn
818 $this->_currentVersion = $current->getVersion();
819 $this->_content = $selected->getPackedContent();
821 $this->locked = $this->page->get('locked');
823 // If author same as previous author, default minor_edit to on.
824 $age = $this->meta['mtime'] - $current->get('mtime');
825 $this->meta['is_minor_edit'] = ($age < MINOR_EDIT_TIMEOUT
826 && $current->get('author') == $user->getId()
829 // Default for new pages is new-style markup.
830 if ($selected->hasDefaultContents())
831 $is_new_markup = true;
833 $is_new_markup = $selected->get('markup') >= 2.0;
835 $this->meta['markup'] = $is_new_markup ? 2.0 : false;
836 $this->meta['pagetype'] = $selected->get('pagetype');
837 if ($this->meta['pagetype'] == 'wikiblog')
838 $this->meta['summary'] = $selected->get('summary'); // keep blog title
840 $this->meta['summary'] = '';
841 $this->editaction = 'edit';
845 class LoadFileConflictPageEditor
848 function editPage($saveFailed = true)
850 $tokens = &$this->tokens;
852 if (!$this->canEdit()) {
853 if ($this->isInitialEdit()) {
854 return $this->viewSource();
856 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
857 } elseif ($this->editaction == 'save') {
858 if ($this->savePage()) {
859 return true; // Page saved.
864 if ($saveFailed || $this->isConcurrentUpdate()) {
865 // Get the text of the original page, and the two conflicting edits
866 // The diff class takes arrays as input. So retrieve content as
867 // an array, or convert it as necesary.
868 $orig = $this->page->getRevision($this->_currentVersion);
869 $this_content = explode("\n", $this->_content);
870 $other_content = $this->current->getContent();
871 require_once 'lib/diff.php';
872 $diff2 = new Diff($other_content, $this_content);
873 $context_lines = max(4, count($other_content) + 1,
874 count($this_content) + 1);
875 $fmt = new BlockDiffFormatter($context_lines);
877 $this->_content = $fmt->format($diff2);
878 // FIXME: integrate this into class BlockDiffFormatter
879 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
881 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
884 $this->_currentVersion = $this->current->getVersion();
885 $this->version = $this->_currentVersion;
886 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
889 if ($this->editaction == 'edit_convert')
890 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
891 if ($this->editaction == 'preview')
892 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
894 // FIXME: NOT_CURRENT_MESSAGE?
895 $tokens = array_merge($tokens, $this->getFormElements());
896 // we need all GET params for loadfile overwrite
897 if ($this->request->getArg('action') == 'loadfile') {
899 $this->tokens['HIDDEN_INPUTS'] =
901 (array('source' => $this->request->getArg('source'),
903 $this->tokens['HIDDEN_INPUTS']);
904 // add two conflict resolution buttons before preview and save.
905 $tokens['PREVIEW_B'] = HTML(
906 Button('submit:edit[keep_old]',
907 _("Keep old"), 'wikiaction'),
909 Button('submit:edit[overwrite]',
910 _("Overwrite with new"), 'wikiaction'),
912 $tokens['PREVIEW_B']);
914 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
915 include_once 'lib/EditToolbar.php';
916 $toolbar = new EditToolbar();
917 $tokens = array_merge($tokens, $toolbar->getTokens());
920 return $this->output('editpage', _("Merge and Edit: %s"));
923 function output($template, $title_fs)
925 $selected = &$this->selected;
926 $current = &$this->current;
928 if ($selected && $selected->getVersion() != $current->getVersion()) {
930 $pagelink = WikiLink($selected);
933 $pagelink = WikiLink($this->page);
936 $title = new FormattedText ($title_fs, $pagelink);
937 $this->tokens['HEADER'] = $title;
938 //hack! there's no TITLE in editpage, but in the previous top template
939 if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
940 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
942 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
943 $this->tokens['PAGE_LOCKED_MESSAGE']);
944 $template = Template($template, $this->tokens);
946 //GeneratePage($template, $title, $rev);
951 function getConflictMessage($unresolved = false)
953 $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.",
956 HTML::p(_("Please check it through before saving."))));
965 // c-hanging-comment-ender-p: nil
966 // indent-tabs-mode: nil