2 require_once 'lib/Template.php';
3 require_once 'lib/WikiUser.php';
11 * @var WikiDB_PageRevision
18 public $_currentVersion;
21 * @var UserPreferences
25 private $_wikicontent;
28 * @param WikiRequest $request
30 function __construct(&$request)
32 $this->request = &$request;
34 $this->user = $request->getUser();
35 $this->page = $request->getPage();
37 $this->current = $this->page->getCurrentRevision(false);
39 // HACKish short circuit to browse on action=create
40 if ($request->getArg('action') == 'create') {
41 if (!$this->current->hasDefaultContents())
42 $request->redirect(WikiURL($this->page->getName())); // noreturn
45 $this->meta = array('author' => $this->user->getId(),
46 'author_id' => $this->user->getAuthenticatedId(),
49 $this->tokens = array();
52 $backend = WYSIWYG_BACKEND;
53 // TODO: error message
54 require_once("lib/WysiwygEdit/$backend.php");
55 $class = "WysiwygEdit_$backend";
56 $this->WysiwygEdit = new $class();
59 require_once 'lib/Captcha.php';
60 $this->Captcha = new Captcha($this->meta);
63 $version = $request->getArg('version');
64 if ($version !== false) {
65 $this->selected = $this->page->getRevision($version);
66 $this->version = $version;
68 $this->version = $this->current->getVersion();
69 $this->selected = $this->page->getRevision($this->version);
72 if ($this->_restoreState()) {
73 $this->_initialEdit = false;
75 $this->_initializeState();
76 $this->_initialEdit = true;
78 // The edit request has specified some initial content from a template
79 if (($template = $request->getArg('template'))
80 and $request->_dbi->isWikiPage($template)
82 $page = $request->_dbi->getPage($template);
83 $current = $page->getCurrentRevision();
84 $this->_content = $current->getPackedContent();
85 } elseif ($initial_content = $request->getArg('initial_content')) {
86 $this->_content = $initial_content;
87 $this->_redirect_to = $request->getArg('save_and_redirect_to');
91 header("Content-Type: text/html; charset=UTF-8");
97 $tokens = &$this->tokens;
98 $tokens['PAGE_LOCKED_MESSAGE'] = '';
99 $tokens['LOCK_CHANGED_MSG'] = '';
100 $tokens['CONCURRENT_UPDATE_MESSAGE'] = '';
101 $r =& $this->request;
103 if (isset($r->args['pref']['editWidth'])
104 and ($r->getPref('editWidth') != $r->args['pref']['editWidth'])
106 $r->_prefs->set('editWidth', $r->args['pref']['editWidth']);
108 if (isset($r->args['pref']['editHeight'])
109 and ($r->getPref('editHeight') != $r->args['pref']['editHeight'])
111 $r->_prefs->set('editHeight', $r->args['pref']['editHeight']);
114 if ($this->isModerated())
115 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getModeratedMessage();
117 if (!$this->canEdit()) {
118 if ($this->isInitialEdit())
119 return $this->viewSource();
120 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
121 } elseif ($r->getArg('save_and_redirect_to') != "") {
122 if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
123 $this->tokens['PAGE_LOCKED_MESSAGE'] =
124 HTML::p(HTML::h1($this->Captcha->failed_msg));
125 } elseif ($this->savePage()) {
128 $request->setArg('action', false);
129 $r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
130 return true; // Page saved.
133 } elseif ($this->editaction == 'save') {
134 if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
135 $this->tokens['PAGE_LOCKED_MESSAGE'] =
136 HTML::p(HTML::h1($this->Captcha->failed_msg));
137 } elseif ($this->savePage()) {
138 return true; // Page saved.
142 } // coming from loadfile conflicts
143 elseif ($this->editaction == 'keep_old') {
144 // keep old page and do nothing
145 $this->_redirectToBrowsePage();
147 } elseif ($this->editaction == 'overwrite') {
148 // take the new content without diff
149 $source = $this->request->getArg('loadfile');
150 require_once 'lib/loadsave.php';
151 $this->request->setArg('loadfile', 1);
152 $this->request->setArg('overwrite', 1);
153 $this->request->setArg('merge', 0);
154 LoadFileOrDir($this->request);
155 $this->_redirectToBrowsePage();
157 } elseif ($this->editaction == 'upload') {
159 $plugin = WikiPluginLoader("UpLoad");
161 // add link to content
165 if ($saveFailed and $this->isConcurrentUpdate()) {
166 // Get the text of the original page, and the two conflicting edits
167 // The diff3 class takes arrays as input. So retrieve content as
168 // an array, or convert it as necesary.
169 $orig = $this->page->getRevision($this->_currentVersion);
170 // FIXME: what if _currentVersion has be deleted?
171 $orig_content = $orig->getContent();
172 $this_content = explode("\n", $this->_content);
173 $other_content = $this->current->getContent();
174 require_once 'lib/diff3.php';
175 $diff = new diff3($orig_content, $this_content, $other_content);
176 $output = $diff->merged_output(_("Your version"), _("Other version"));
177 // Set the content of the textarea to the merged diff
178 // output, and update the version
179 $this->_content = implode("\n", $output);
180 $this->_currentVersion = $this->current->getVersion();
181 $this->version = $this->_currentVersion;
182 $unresolved = $diff->ConflictingBlocks;
183 $tokens['CONCURRENT_UPDATE_MESSAGE']
184 = $this->getConflictMessage($unresolved);
185 } elseif ($saveFailed && !$this->_isSpam) {
186 $tokens['CONCURRENT_UPDATE_MESSAGE'] =
187 HTML(HTML::h2(_("Some internal editing error")),
188 HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
189 HTML::p(HTML::em(_("&version=-1 might help."))));
192 if ($this->editaction == 'edit_convert')
193 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
194 if ($this->editaction == 'preview')
195 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
196 if ($this->editaction == 'diff')
197 $tokens['PREVIEW_CONTENT'] = $this->getDiff();
199 // FIXME: NOT_CURRENT_MESSAGE?
200 $tokens = array_merge($tokens, $this->getFormElements());
202 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
203 require_once 'lib/EditToolbar.php';
204 $toolbar = new EditToolbar();
205 $tokens = array_merge($tokens, $toolbar->getTokens());
207 $tokens['EDIT_TOOLBAR'] = '';
210 return $this->output('editpage', _("Edit: %s"));
213 function output($template, $title_fs)
216 $selected = &$this->selected;
217 $current = &$this->current;
219 if ($selected && $selected->getVersion() != $current->getVersion()) {
221 $pagelink = WikiLink($selected);
224 $pagelink = WikiLink($this->page);
227 $title = new FormattedText ($title_fs, $pagelink);
228 // not for dumphtml or viewsource
229 if (ENABLE_WYSIWYG and $template == 'editpage') {
230 $WikiTheme->addMoreHeaders($this->WysiwygEdit->Head());
231 //$tokens['PAGE_SOURCE'] = $this->WysiwygEdit->ConvertBefore($this->_content);
233 $template = Template($template, $this->tokens);
234 /* Tell google (and others) not to take notice of edit links */
235 if (GOOGLE_LINKS_NOFOLLOW)
236 $args = array('ROBOTS_META' => "noindex,nofollow");
237 GeneratePage($template, $title, $rev);
241 function viewSource()
243 assert($this->isInitialEdit());
244 assert($this->selected);
246 $this->tokens['PAGE_SOURCE'] = $this->_content;
247 $this->tokens['HIDDEN_INPUTS'] = HiddenInputs($this->request->getArgs());
248 return $this->output('viewsource', _("View Source: %s"));
251 function updateLock()
254 if (!ENABLE_PAGE_PUBLIC && !ENABLE_EXTERNAL_PAGES) {
255 if ((bool)$this->page->get('locked') == (bool)$this->locked)
256 return false; // Not changed.
259 if (!$this->user->isAdmin()) {
260 // FIXME: some sort of message
261 return false; // not allowed.
263 if ((bool)$this->page->get('locked') != (bool)$this->locked) {
264 $this->page->set('locked', (bool)$this->locked);
265 $this->tokens['LOCK_CHANGED_MSG']
267 ? _("Page now locked.")
268 : _("Page now unlocked.") . " ");
271 if (ENABLE_PAGE_PUBLIC and (bool)$this->page->get('public') != (bool)$this->public) {
272 $this->page->set('public', (bool)$this->public);
273 $this->tokens['LOCK_CHANGED_MSG']
275 ? _("Page now public.")
276 : _("Page now not-public."));
280 if (ENABLE_EXTERNAL_PAGES) {
281 if ((bool)$this->page->get('external') != (bool)$this->external) {
282 $this->page->set('external', (bool)$this->external);
283 $this->tokens['LOCK_CHANGED_MSG']
285 ? _("Page now external.")
286 : _("Page now not-external.")) . " ";
290 return $changed; // lock changed.
295 $request = &$this->request;
297 if ($this->isUnchanged()) {
298 // Allow admin lock/unlock even if
299 // no text changes were made.
300 if ($this->updateLock()) {
301 $dbi = $request->getDbh();
304 // Save failed. No changes made.
305 $this->_redirectToBrowsePage();
309 if (!$this->user->isAdmin() and $this->isSpam()) {
310 $this->_isSpam = true;
314 $page = &$this->page;
316 // Include any meta-data from original page version which
317 // has not been explicitly updated.
318 $meta = $this->selected->getMetaData();
319 $meta = array_merge($meta, $this->meta);
322 $this->_content = $this->getContent();
323 $newrevision = $page->save($this->_content,
326 : $this->_currentVersion + 1,
329 if (!isa($newrevision, 'WikiDB_PageRevision')) {
330 // Save failed. (Concurrent updates).
334 // New contents successfully saved...
337 // Clean out archived versions of this page.
338 require_once 'lib/ArchiveCleaner.php';
339 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
340 $cleaner->cleanPageRevisions($page);
342 /* generate notification emails done in WikiDB::save to catch
343 all direct calls (admin plugins) */
345 // look at the errorstack
346 $errors = $GLOBALS['ErrorManager']->_postponed_errors;
347 $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
348 $GLOBALS['ErrorManager']->_postponed_errors = $errors;
350 $dbi = $request->getDbh();
354 if (empty($warnings->_content) && !$WikiTheme->getImageURL('signature')) {
355 // Do redirect to browse page if no signature has
356 // been defined. In this case, the user will most
357 // likely not see the rest of the HTML we generate
359 $request->setArg('action', false);
360 $this->_redirectToBrowsePage();
364 // Force browse of current page version.
365 $request->setArg('version', false);
366 // testme: does preview and more need action=edit?
367 $request->setArg('action', false);
369 $template = Template('savepage', $this->tokens);
370 $template->replace('CONTENT', $newrevision->getTransformedContent());
371 if (!empty($warnings->_content)) {
372 $template->replace('WARNINGS', $warnings);
373 unset($GLOBALS['ErrorManager']->_postponed_errors);
376 $pagelink = WikiLink($page);
378 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
382 function isConcurrentUpdate()
384 assert($this->current->getVersion() >= $this->_currentVersion);
385 return $this->current->getVersion() != $this->_currentVersion;
390 return !$this->page->get('locked') || $this->user->isAdmin();
393 function isInitialEdit()
395 return $this->_initialEdit;
398 function isUnchanged()
400 $current = &$this->current;
401 return $this->_content == $current->getPackedContent();
405 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
406 * Need to check dynamically some blacklist wikipage settings
407 * (plugin WikiAccessRestrictions) and some static blacklist.
409 * More than NUM_SPAM_LINKS (default: 20) new external links.
410 * Disabled if NUM_SPAM_LINKS is 0
411 * ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
412 * ENABLE_SPAMBLOCKLIST: content domain blacklist
416 $current = &$this->current;
417 $request = &$this->request;
419 $oldtext = $current->getPackedContent();
420 $newtext =& $this->_content;
421 $numlinks = $this->numLinks($newtext);
422 $newlinks = $numlinks - $this->numLinks($oldtext);
423 // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
424 // better use a certain text : link ratio.
426 // 1. Not more than NUM_SPAM_LINKS (default: 20) new external links
427 if ((NUM_SPAM_LINKS > 0) and ($newlinks >= NUM_SPAM_LINKS)) {
428 // Allow strictly authenticated users?
429 // TODO: mail the admin?
430 $this->tokens['PAGE_LOCKED_MESSAGE'] =
431 HTML($this->getSpamMessage(),
432 HTML::p(HTML::strong(_("Too many external links."))));
435 // 2. external babycart (SpamAssassin) check
436 // This will probably prevent from discussing sex or viagra related topics. So beware.
437 if (ENABLE_SPAMASSASSIN) {
438 require_once 'lib/spam_babycart.php';
439 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
440 $this->user->getId())
442 // TODO: mail the admin
443 if (is_array($babycart))
444 $this->tokens['PAGE_LOCKED_MESSAGE'] =
445 HTML($this->getSpamMessage(),
446 HTML::p(HTML::em(_("SpamAssassin reports: "),
447 join("\n", $babycart))));
451 // 3. extract (new) links and check surbl for blocked domains
452 if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
453 require_once 'lib/SpamBlocklist.php';
454 require_once 'lib/InlineParser.php';
455 $parsed = TransformLinks($newtext);
456 $oldparsed = TransformLinks($oldtext);
458 foreach ($oldparsed->_content as $link) {
459 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
460 $uri = $link->_getURL($this->page->getName());
465 foreach ($parsed->_content as $link) {
466 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
467 $uri = $link->_getURL($this->page->getName());
468 // only check new links, so admins may add blocked links.
469 if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
470 // TODO: mail the admin
471 $this->tokens['PAGE_LOCKED_MESSAGE'] =
472 HTML($this->getSpamMessage(),
473 HTML::p(HTML::strong(_("External links contain blocked domains:")),
474 HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
475 $uri . " [" . $res[2] . "]", $res[0], $res[1])))));
488 /** Number of external links in the wikitext
490 function numLinks(&$text)
492 return substr_count($text, "http://") + substr_count($text, "https://");
495 /** Header of the Anti Spam message
497 function getSpamMessage()
500 HTML(HTML::h2(_("Spam Prevention")),
501 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
503 _("Sorry for the inconvenience.")),
507 function getPreview()
509 require_once 'lib/PageType.php';
510 $this->_content = $this->getContent();
511 return new TransformedText($this->page, $this->_content, $this->meta);
514 function getConvertedPreview()
516 require_once 'lib/PageType.php';
517 $this->_content = $this->getContent();
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(array('class' => 'warning_msg'),
530 _("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[locked]',
672 'id' => 'edit-locked',
673 'disabled' => (bool)!$this->user->isAdmin(),
674 'checked' => (bool)$this->locked));
675 if (ENABLE_PAGE_PUBLIC) {
677 = HTML::input(array('type' => 'checkbox',
678 'name' => 'edit[public]',
679 'id' => 'edit-public',
680 'disabled' => (bool)!$this->user->isAdmin(),
681 'checked' => (bool)$this->page->get('public')));
683 if (ENABLE_EXTERNAL_PAGES) {
685 = HTML::input(array('type' => 'checkbox',
686 'name' => 'edit[external]',
687 'id' => 'edit-external',
688 'disabled' => (bool)!$this->user->isAdmin(),
689 'checked' => (bool)$this->page->get('external')));
691 if (ENABLE_WYSIWYG) {
692 if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
693 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
697 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
699 array('accesskey' => 'p',
700 'title' => _('Preview the current content [alt-p]')));
702 //if (!$this->isConcurrentUpdate() && $this->canEdit())
703 $el['SAVE_B'] = Button('submit:edit[save]',
704 _("Save"), 'wikiaction',
705 array('accesskey' => 's',
706 'title' => _('Save the current content as wikipage [alt-s]')));
707 $el['CHANGES_B'] = Button('submit:edit[diff]',
708 _("Changes"), 'wikiaction',
709 array('accesskey' => 'c',
710 'title' => _('Preview the current changes as diff [alt-c]')));
711 $el['UPLOAD_B'] = Button('submit:edit[upload]',
712 _("Upload"), 'wikiaction',
713 array('title' => _('Select a local file and press Upload to attach into this page')));
714 $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
715 _("Spell Check"), 'wikiaction',
716 array('title' => _('Check the spelling')));
717 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
720 = HTML::input(array('type' => 'text',
723 'class' => "numeric",
724 'name' => 'pref[editWidth]',
725 'id' => 'pref-editWidth',
726 'value' => $request->getPref('editWidth'),
727 'onchange' => 'this.form.submit();'));
729 = HTML::input(array('type' => 'text',
732 'class' => "numeric",
733 'name' => 'pref[editHeight]',
734 'id' => 'pref-editHeight',
735 'value' => $request->getPref('editHeight'),
736 'onchange' => 'this.form.submit();'));
737 $el['SEP'] = $WikiTheme->getButtonSeparator();
738 $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
739 HTML::em($this->user->getId()));
744 private function _redirectToBrowsePage()
746 $this->request->redirect(WikiURL($this->page, array(), 'absolute_url'));
749 function _restoreState()
751 $request = &$this->request;
753 $posted = $request->getArg('edit');
754 $request->setArg('edit', false);
757 || !$request->isPost()
758 || !in_array($request->getArg('action'), array('edit', 'loadfile'))
762 if (!isset($posted['content']) || !is_string($posted['content']))
764 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
765 rtrim($posted['content']));
766 $this->_content = $this->getContent();
768 $this->_currentVersion = (int)$posted['current_version'];
770 if ($this->_currentVersion < 0)
772 if ($this->_currentVersion > $this->current->getVersion())
773 return false; // FIXME: some kind of warning?
775 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
776 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
777 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
779 $meta['captcha_input'] = !empty($posted['captcha_input']) ?
780 $posted['captcha_input'] : '';
782 $this->meta = array_merge($this->meta, $meta);
783 $this->locked = !empty($posted['locked']);
784 if (ENABLE_PAGE_PUBLIC)
785 $this->public = !empty($posted['public']);
786 if (ENABLE_EXTERNAL_PAGES)
787 $this->external = !empty($posted['external']);
789 foreach (array('preview', 'save', 'edit_convert',
790 'keep_old', 'overwrite', 'diff', 'upload') as $o) {
791 if (!empty($posted[$o]))
792 $this->editaction = $o;
794 if (empty($this->editaction))
795 $this->editaction = 'edit';
800 function _initializeState()
802 $request = &$this->request;
803 $current = &$this->current;
804 $selected = &$this->selected;
805 $user = &$this->user;
808 NoSuchRevision($request, $this->page, $this->version); // noreturn
810 $this->_currentVersion = $current->getVersion();
811 $this->_content = $selected->getPackedContent();
813 $this->locked = $this->page->get('locked');
815 // If author same as previous author, default minor_edit to on.
816 $age = $this->meta['mtime'] - $current->get('mtime');
817 $this->meta['is_minor_edit'] = ($age < MINOR_EDIT_TIMEOUT
818 && $current->get('author') == $user->getId()
821 $this->meta['pagetype'] = $selected->get('pagetype');
822 if ($this->meta['pagetype'] == 'wikiblog')
823 $this->meta['summary'] = $selected->get('summary'); // keep blog title
825 $this->meta['summary'] = '';
826 $this->editaction = 'edit';
830 class LoadFileConflictPageEditor
833 function editPage($saveFailed = true)
835 $tokens = &$this->tokens;
837 if (!$this->canEdit()) {
838 if ($this->isInitialEdit()) {
839 return $this->viewSource();
841 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
842 } elseif ($this->editaction == 'save') {
843 if ($this->savePage()) {
844 return true; // Page saved.
849 if ($saveFailed || $this->isConcurrentUpdate()) {
850 // Get the text of the original page, and the two conflicting edits
851 // The diff class takes arrays as input. So retrieve content as
852 // an array, or convert it as necesary.
853 $orig = $this->page->getRevision($this->_currentVersion);
854 $this_content = explode("\n", $this->_content);
855 $other_content = $this->current->getContent();
856 require_once 'lib/diff.php';
857 $diff2 = new Diff($other_content, $this_content);
858 $context_lines = max(4, count($other_content) + 1,
859 count($this_content) + 1);
860 $fmt = new BlockDiffFormatter($context_lines);
862 $this->_content = $fmt->format($diff2);
863 // FIXME: integrate this into class BlockDiffFormatter
864 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
866 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
869 $this->_currentVersion = $this->current->getVersion();
870 $this->version = $this->_currentVersion;
871 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
874 if ($this->editaction == 'edit_convert')
875 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
876 if ($this->editaction == 'preview')
877 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
879 // FIXME: NOT_CURRENT_MESSAGE?
880 $tokens = array_merge($tokens, $this->getFormElements());
881 // we need all GET params for loadfile overwrite
882 if ($this->request->getArg('action') == 'loadfile') {
884 $this->tokens['HIDDEN_INPUTS'] =
886 (array('source' => $this->request->getArg('source'),
888 $this->tokens['HIDDEN_INPUTS']);
889 // add two conflict resolution buttons before preview and save.
890 $tokens['PREVIEW_B'] = HTML(
891 Button('submit:edit[keep_old]',
892 _("Keep old"), 'wikiaction'),
894 Button('submit:edit[overwrite]',
895 _("Overwrite with new"), 'wikiaction'),
897 $tokens['PREVIEW_B']);
899 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
900 include_once 'lib/EditToolbar.php';
901 $toolbar = new EditToolbar();
902 $tokens = array_merge($tokens, $toolbar->getTokens());
905 return $this->output('editpage', _("Merge and Edit: %s"));
908 function output($template, $title_fs)
910 $selected = &$this->selected;
911 $current = &$this->current;
913 if ($selected && $selected->getVersion() != $current->getVersion()) {
914 $pagelink = WikiLink($selected);
916 $pagelink = WikiLink($this->page);
919 $title = new FormattedText ($title_fs, $pagelink);
920 $this->tokens['HEADER'] = $title;
921 //hack! there's no TITLE in editpage, but in the previous top template
922 if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
923 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
925 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
926 $this->tokens['PAGE_LOCKED_MESSAGE']);
927 $template = Template($template, $this->tokens);
929 //GeneratePage($template, $title, $rev);
934 function getConflictMessage($unresolved = false)
936 $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.",
939 HTML::p(_("Please check it through before saving."))));
948 // c-hanging-comment-ender-p: nil
949 // indent-tabs-mode: nil