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=UTF-8");
73 $tokens = &$this->tokens;
74 $tokens['PAGE_LOCKED_MESSAGE'] = '';
75 $tokens['LOCK_CHANGED_MSG'] = '';
76 $tokens['CONCURRENT_UPDATE_MESSAGE'] = '';
79 if (isset($r->args['pref']['editWidth'])
80 and ($r->getPref('editWidth') != $r->args['pref']['editWidth'])
82 $r->_prefs->set('editWidth', $r->args['pref']['editWidth']);
84 if (isset($r->args['pref']['editHeight'])
85 and ($r->getPref('editHeight') != $r->args['pref']['editHeight'])
87 $r->_prefs->set('editHeight', $r->args['pref']['editHeight']);
90 if ($this->isModerated())
91 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getModeratedMessage();
93 if (!$this->canEdit()) {
94 if ($this->isInitialEdit())
95 return $this->viewSource();
96 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
97 } elseif ($r->getArg('save_and_redirect_to') != "") {
98 if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
99 $this->tokens['PAGE_LOCKED_MESSAGE'] =
100 HTML::p(HTML::h1($this->Captcha->failed_msg));
101 } elseif ($this->savePage()) {
103 $request->setArg('action', false);
104 $r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
105 return true; // Page saved.
108 } elseif ($this->editaction == 'save') {
109 if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
110 $this->tokens['PAGE_LOCKED_MESSAGE'] =
111 HTML::p(HTML::h1($this->Captcha->failed_msg));
112 } elseif ($this->savePage()) {
113 return true; // Page saved.
117 } // coming from loadfile conflicts
118 elseif ($this->editaction == 'keep_old') {
119 // keep old page and do nothing
120 $this->_redirectToBrowsePage();
121 //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
123 } elseif ($this->editaction == 'overwrite') {
124 // take the new content without diff
125 $source = $this->request->getArg('loadfile');
126 require_once 'lib/loadsave.php';
127 $this->request->setArg('loadfile', 1);
128 $this->request->setArg('overwrite', 1);
129 $this->request->setArg('merge', 0);
130 LoadFileOrDir($this->request);
131 $this->_redirectToBrowsePage();
132 //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
134 } elseif ($this->editaction == 'upload') {
136 $plugin = WikiPluginLoader("UpLoad");
138 // add link to content
142 if ($saveFailed and $this->isConcurrentUpdate()) {
143 // Get the text of the original page, and the two conflicting edits
144 // The diff3 class takes arrays as input. So retrieve content as
145 // an array, or convert it as necesary.
146 $orig = $this->page->getRevision($this->_currentVersion);
147 // FIXME: what if _currentVersion has be deleted?
148 $orig_content = $orig->getContent();
149 $this_content = explode("\n", $this->_content);
150 $other_content = $this->current->getContent();
151 require_once 'lib/diff3.php';
152 $diff = new diff3($orig_content, $this_content, $other_content);
153 $output = $diff->merged_output(_("Your version"), _("Other version"));
154 // Set the content of the textarea to the merged diff
155 // output, and update the version
156 $this->_content = implode("\n", $output);
157 $this->_currentVersion = $this->current->getVersion();
158 $this->version = $this->_currentVersion;
159 $unresolved = $diff->ConflictingBlocks;
160 $tokens['CONCURRENT_UPDATE_MESSAGE']
161 = $this->getConflictMessage($unresolved);
162 } elseif ($saveFailed && !$this->_isSpam) {
163 $tokens['CONCURRENT_UPDATE_MESSAGE'] =
164 HTML(HTML::h2(_("Some internal editing error")),
165 HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
166 HTML::p(HTML::em(_("&version=-1 might help."))));
169 if ($this->editaction == 'edit_convert')
170 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
171 if ($this->editaction == 'preview')
172 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
173 if ($this->editaction == 'diff')
174 $tokens['PREVIEW_CONTENT'] = $this->getDiff();
176 // FIXME: NOT_CURRENT_MESSAGE?
177 $tokens = array_merge($tokens, $this->getFormElements());
179 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
180 require_once 'lib/EditToolbar.php';
181 $toolbar = new EditToolbar();
182 $tokens = array_merge($tokens, $toolbar->getTokens());
184 $tokens['EDIT_TOOLBAR'] = '';
187 return $this->output('editpage', _("Edit: %s"));
190 function output($template, $title_fs)
193 $selected = &$this->selected;
194 $current = &$this->current;
196 if ($selected && $selected->getVersion() != $current->getVersion()) {
198 $pagelink = WikiLink($selected);
201 $pagelink = WikiLink($this->page);
204 $title = new FormattedText ($title_fs, $pagelink);
205 // not for dumphtml or viewsource
206 if (ENABLE_WYSIWYG and $template == 'editpage') {
207 $WikiTheme->addMoreHeaders($this->WysiwygEdit->Head());
208 //$tokens['PAGE_SOURCE'] = $this->WysiwygEdit->ConvertBefore($this->_content);
210 $template = Template($template, $this->tokens);
211 /* Tell google (and others) not to take notice of edit links */
212 if (GOOGLE_LINKS_NOFOLLOW)
213 $args = array('ROBOTS_META' => "noindex,nofollow");
214 GeneratePage($template, $title, $rev);
218 function viewSource()
220 assert($this->isInitialEdit());
221 assert($this->selected);
223 $this->tokens['PAGE_SOURCE'] = $this->_content;
224 $this->tokens['HIDDEN_INPUTS'] = HiddenInputs($this->request->getArgs());
225 return $this->output('viewsource', _("View Source: %s"));
228 function updateLock()
231 if (!ENABLE_PAGE_PUBLIC && !ENABLE_EXTERNAL_PAGES) {
232 if ((bool)$this->page->get('locked') == (bool)$this->locked)
233 return false; // Not changed.
236 if (!$this->user->isAdmin()) {
237 // FIXME: some sort of message
238 return false; // not allowed.
240 if ((bool)$this->page->get('locked') != (bool)$this->locked) {
241 $this->page->set('locked', (bool)$this->locked);
242 $this->tokens['LOCK_CHANGED_MSG']
244 ? _("Page now locked.")
245 : _("Page now unlocked.") . " ");
248 if (ENABLE_PAGE_PUBLIC and (bool)$this->page->get('public') != (bool)$this->public) {
249 $this->page->set('public', (bool)$this->public);
250 $this->tokens['LOCK_CHANGED_MSG']
252 ? _("Page now public.")
253 : _("Page now not-public."));
257 if (ENABLE_EXTERNAL_PAGES) {
258 if ((bool)$this->page->get('external') != (bool)$this->external) {
259 $this->page->set('external', (bool)$this->external);
260 $this->tokens['LOCK_CHANGED_MSG']
262 ? _("Page now external.")
263 : _("Page now not-external.")) . " ";
267 return $changed; // lock changed.
272 $request = &$this->request;
274 if ($this->isUnchanged()) {
275 // Allow admin lock/unlock even if
276 // no text changes were made.
277 if ($this->updateLock()) {
278 $dbi = $request->getDbh();
281 // Save failed. No changes made.
282 $this->_redirectToBrowsePage();
283 // user will probably not see the rest of this...
284 require_once 'lib/display.php';
285 // force browse of current version:
286 $request->setArg('action', false);
287 $request->setArg('version', false);
288 displayPage($request, 'nochanges');
292 if (!$this->user->isAdmin() and $this->isSpam()) {
293 $this->_isSpam = true;
296 // Save failed. No changes made.
297 $this->_redirectToBrowsePage();
298 // user will probably not see the rest of this...
299 require_once 'lib/display.php';
300 // force browse of current version:
301 $request->setArg('version', false);
302 displayPage($request, 'nochanges');
307 $page = &$this->page;
309 // Include any meta-data from original page version which
310 // has not been explicitly updated.
311 $meta = $this->selected->getMetaData();
312 $meta = array_merge($meta, $this->meta);
315 $this->_content = $this->getContent();
316 $newrevision = $page->save($this->_content,
319 : $this->_currentVersion + 1,
322 if (!isa($newrevision, 'WikiDB_PageRevision')) {
323 // Save failed. (Concurrent updates).
327 // New contents successfully saved...
330 // Clean out archived versions of this page.
331 require_once 'lib/ArchiveCleaner.php';
332 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
333 $cleaner->cleanPageRevisions($page);
335 /* generate notification emails done in WikiDB::save to catch
336 all direct calls (admin plugins) */
338 // look at the errorstack
339 $errors = $GLOBALS['ErrorManager']->_postponed_errors;
340 $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
341 $GLOBALS['ErrorManager']->_postponed_errors = $errors;
343 $dbi = $request->getDbh();
347 if (empty($warnings->_content) && !$WikiTheme->getImageURL('signature')) {
348 // Do redirect to browse page if no signature has
349 // been defined. In this case, the user will most
350 // likely not see the rest of the HTML we generate
352 $request->setArg('action', false);
353 $this->_redirectToBrowsePage();
356 // Force browse of current page version.
357 $request->setArg('version', false);
358 // testme: does preview and more need action=edit?
359 $request->setArg('action', false);
361 $template = Template('savepage', $this->tokens);
362 $template->replace('CONTENT', $newrevision->getTransformedContent());
363 if (!empty($warnings->_content)) {
364 $template->replace('WARNINGS', $warnings);
365 unset($GLOBALS['ErrorManager']->_postponed_errors);
368 $pagelink = WikiLink($page);
370 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
374 function isConcurrentUpdate()
376 assert($this->current->getVersion() >= $this->_currentVersion);
377 return $this->current->getVersion() != $this->_currentVersion;
382 return !$this->page->get('locked') || $this->user->isAdmin();
385 function isInitialEdit()
387 return $this->_initialEdit;
390 function isUnchanged()
392 $current = &$this->current;
393 return $this->_content == $current->getPackedContent();
397 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
398 * Need to check dynamically some blacklist wikipage settings
399 * (plugin WikiAccessRestrictions) and some static blacklist.
401 * More than NUM_SPAM_LINKS (default: 20) new external links.
402 * Disabled if NUM_SPAM_LINKS is 0
403 * ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
404 * ENABLE_SPAMBLOCKLIST: content domain blacklist
408 $current = &$this->current;
409 $request = &$this->request;
411 $oldtext = $current->getPackedContent();
412 $newtext =& $this->_content;
413 $numlinks = $this->numLinks($newtext);
414 $newlinks = $numlinks - $this->numLinks($oldtext);
415 // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
416 // better use a certain text : link ratio.
418 // 1. Not more than NUM_SPAM_LINKS (default: 20) new external links
419 if ((NUM_SPAM_LINKS > 0) and ($newlinks >= NUM_SPAM_LINKS)) {
420 // Allow strictly authenticated users?
421 // TODO: mail the admin?
422 $this->tokens['PAGE_LOCKED_MESSAGE'] =
423 HTML($this->getSpamMessage(),
424 HTML::p(HTML::strong(_("Too many external links."))));
427 // 2. external babycart (SpamAssassin) check
428 // This will probably prevent from discussing sex or viagra related topics. So beware.
429 if (ENABLE_SPAMASSASSIN) {
430 require_once 'lib/spam_babycart.php';
431 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
432 $this->user->getId())
434 // TODO: mail the admin
435 if (is_array($babycart))
436 $this->tokens['PAGE_LOCKED_MESSAGE'] =
437 HTML($this->getSpamMessage(),
438 HTML::p(HTML::em(_("SpamAssassin reports: "),
439 join("\n", $babycart))));
443 // 3. extract (new) links and check surbl for blocked domains
444 if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
445 require_once 'lib/SpamBlocklist.php';
446 require_once 'lib/InlineParser.php';
447 $parsed = TransformLinks($newtext);
448 $oldparsed = TransformLinks($oldtext);
450 foreach ($oldparsed->_content as $link) {
451 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
452 $uri = $link->_getURL($this->page->getName());
457 foreach ($parsed->_content as $link) {
458 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
459 $uri = $link->_getURL($this->page->getName());
460 // only check new links, so admins may add blocked links.
461 if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
462 // TODO: mail the admin
463 $this->tokens['PAGE_LOCKED_MESSAGE'] =
464 HTML($this->getSpamMessage(),
465 HTML::p(HTML::strong(_("External links contain blocked domains:")),
466 HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
467 $uri . " [" . $res[2] . "]", $res[0], $res[1])))));
480 /** Number of external links in the wikitext
482 function numLinks(&$text)
484 return substr_count($text, "http://") + substr_count($text, "https://");
487 /** Header of the Anti Spam message
489 function getSpamMessage()
492 HTML(HTML::h2(_("Spam Prevention")),
493 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
495 _("Sorry for the inconvenience.")),
499 function getPreview()
501 require_once 'lib/PageType.php';
502 $this->_content = $this->getContent();
503 return new TransformedText($this->page, $this->_content, $this->meta);
506 function getConvertedPreview()
508 require_once 'lib/PageType.php';
509 $this->_content = $this->getContent();
510 return new TransformedText($this->page, $this->_content, $this->meta);
515 require_once 'lib/diff.php';
518 $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
519 if ($diff->isEmpty()) {
520 $html->pushContent(HTML::hr(),
521 HTML::p(array('class' => 'warning_msg'),
522 _("Versions are identical")));
524 // New CSS formatted unified diffs
525 $fmt = new HtmlUnifiedDiffFormatter;
526 $html->pushContent($fmt->format($diff));
531 // possibly convert HTMLAREA content back to Wiki markup
532 function getContent()
534 if (ENABLE_WYSIWYG) {
535 // don't store everything as html
536 if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
537 // Wikiwyg shortcut to avoid the InlineTransformer:
538 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
539 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
540 $this->_content = join("", $xml_output->_content);
542 $this->meta['pagetype'] = 'html';
544 return $this->_content;
546 return $this->_content;
550 function getLockedMessage()
553 HTML(HTML::h2(_("Page Locked")),
554 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
555 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
556 HTML::p(_("Sorry for the inconvenience.")));
559 function isModerated()
561 return $this->page->get('moderation');
564 function getModeratedMessage()
567 HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
568 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")))),
569 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.",
570 WikiLink(_("UserPreferences")))));
573 function getConflictMessage($unresolved = false)
576 xgettext only knows about c/c++ line-continuation strings
577 it does not know about php's dot operator.
578 We want to translate this entire paragraph as one string, of course.
581 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
584 $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.",
585 "<<<<<<< " . _("Your version"),
586 ">>>>>>> " . _("Other version")));
588 $message = HTML::p(_("Please check it through before saving."));
590 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
591 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
593 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
594 HTML::li(_("Save your updated changes.")));
597 HTML(HTML::h2(_("Conflicting Edits!")),
598 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
599 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.")),
603 function getTextArea()
605 $request = &$this->request;
607 $readonly = !$this->canEdit(); // || $this->isConcurrentUpdate();
609 // WYSIWYG will need two pagetypes: raw wikitest and converted html
610 if (ENABLE_WYSIWYG) {
611 $this->_wikicontent = $this->_content;
612 $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
613 // $this->getPreview();
614 //$this->_htmlcontent = $this->_content->asXML();
617 $textarea = HTML::textarea(array('class' => 'wikiedit',
618 'name' => 'edit[content]',
619 'id' => 'edit-content',
620 'rows' => $request->getPref('editHeight'),
621 'cols' => $request->getPref('editWidth'),
622 'readonly' => (bool)$readonly),
624 if (ENABLE_WYSIWYG) {
625 return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
626 $textarea->getAttr('name'));
631 function getFormElements()
634 $request = &$this->request;
635 $page = &$this->page;
637 $h = array('action' => 'edit',
638 'pagename' => $page->getName(),
639 'version' => $this->version,
640 'edit[pagetype]' => $this->meta['pagetype'],
641 'edit[current_version]' => $this->_currentVersion);
643 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
644 $el['EDIT_TEXTAREA'] = $this->getTextArea();
645 if (ENABLE_CAPTCHA) {
646 $el = array_merge($el, $this->Captcha->getFormElements());
649 = HTML::input(array('type' => 'text',
650 'class' => 'wikitext',
651 'id' => 'edit-summary',
652 'name' => 'edit[summary]',
655 'value' => $this->meta['summary']));
657 = HTML::input(array('type' => 'checkbox',
658 'name' => 'edit[minor_edit]',
659 'id' => 'edit-minor_edit',
660 'checked' => (bool)$this->meta['is_minor_edit']));
662 = HTML::input(array('type' => 'checkbox',
663 'name' => 'edit[locked]',
664 'id' => 'edit-locked',
665 'disabled' => (bool)!$this->user->isAdmin(),
666 'checked' => (bool)$this->locked));
667 if (ENABLE_PAGE_PUBLIC) {
669 = HTML::input(array('type' => 'checkbox',
670 'name' => 'edit[public]',
671 'id' => 'edit-public',
672 'disabled' => (bool)!$this->user->isAdmin(),
673 'checked' => (bool)$this->page->get('public')));
675 if (ENABLE_EXTERNAL_PAGES) {
677 = HTML::input(array('type' => 'checkbox',
678 'name' => 'edit[external]',
679 'id' => 'edit-external',
680 'disabled' => (bool)!$this->user->isAdmin(),
681 'checked' => (bool)$this->page->get('external')));
683 if (ENABLE_WYSIWYG) {
684 if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
685 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
689 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
691 array('accesskey' => 'p',
692 'title' => 'Preview the current content [alt-p]'));
694 //if (!$this->isConcurrentUpdate() && $this->canEdit())
695 $el['SAVE_B'] = Button('submit:edit[save]',
696 _("Save"), 'wikiaction',
697 array('accesskey' => 's',
698 'title' => 'Save the current content as wikipage [alt-s]'));
699 $el['CHANGES_B'] = Button('submit:edit[diff]',
700 _("Changes"), 'wikiaction',
701 array('accesskey' => 'c',
702 'title' => 'Preview the current changes as diff [alt-c]'));
703 $el['UPLOAD_B'] = Button('submit:edit[upload]',
704 _("Upload"), 'wikiaction',
705 array('title' => 'Select a local file and press Upload to attach into this page'));
706 $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
707 _("Spell Check"), 'wikiaction',
708 array('title' => 'Check the spelling'));
709 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
712 = HTML::input(array('type' => 'text',
715 'class' => "numeric",
716 'name' => 'pref[editWidth]',
717 'id' => 'pref-editWidth',
718 'value' => $request->getPref('editWidth'),
719 'onchange' => 'this.form.submit();'));
721 = HTML::input(array('type' => 'text',
724 'class' => "numeric",
725 'name' => 'pref[editHeight]',
726 'id' => 'pref-editHeight',
727 'value' => $request->getPref('editHeight'),
728 'onchange' => 'this.form.submit();'));
729 $el['SEP'] = $WikiTheme->getButtonSeparator();
730 $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
731 HTML::em($this->user->getId()));
736 function _redirectToBrowsePage()
738 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
741 function _restoreState()
743 $request = &$this->request;
745 $posted = $request->getArg('edit');
746 $request->setArg('edit', false);
749 || !$request->isPost()
750 || !in_array($request->getArg('action'), array('edit', 'loadfile'))
754 if (!isset($posted['content']) || !is_string($posted['content']))
756 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
757 rtrim($posted['content']));
758 $this->_content = $this->getContent();
760 $this->_currentVersion = (int)$posted['current_version'];
762 if ($this->_currentVersion < 0)
764 if ($this->_currentVersion > $this->current->getVersion())
765 return false; // FIXME: some kind of warning?
767 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
768 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
769 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
771 $meta['captcha_input'] = !empty($posted['captcha_input']) ?
772 $posted['captcha_input'] : '';
774 $this->meta = array_merge($this->meta, $meta);
775 $this->locked = !empty($posted['locked']);
776 if (ENABLE_PAGE_PUBLIC)
777 $this->public = !empty($posted['public']);
778 if (ENABLE_EXTERNAL_PAGES)
779 $this->external = !empty($posted['external']);
781 foreach (array('preview', 'save', 'edit_convert',
782 'keep_old', 'overwrite', 'diff', 'upload') as $o) {
783 if (!empty($posted[$o]))
784 $this->editaction = $o;
786 if (empty($this->editaction))
787 $this->editaction = 'edit';
792 function _initializeState()
794 $request = &$this->request;
795 $current = &$this->current;
796 $selected = &$this->selected;
797 $user = &$this->user;
800 NoSuchRevision($request, $this->page, $this->version); // noreturn
802 $this->_currentVersion = $current->getVersion();
803 $this->_content = $selected->getPackedContent();
805 $this->locked = $this->page->get('locked');
807 // If author same as previous author, default minor_edit to on.
808 $age = $this->meta['mtime'] - $current->get('mtime');
809 $this->meta['is_minor_edit'] = ($age < MINOR_EDIT_TIMEOUT
810 && $current->get('author') == $user->getId()
813 $this->meta['pagetype'] = $selected->get('pagetype');
814 if ($this->meta['pagetype'] == 'wikiblog')
815 $this->meta['summary'] = $selected->get('summary'); // keep blog title
817 $this->meta['summary'] = '';
818 $this->editaction = 'edit';
822 class LoadFileConflictPageEditor
825 function editPage($saveFailed = true)
827 $tokens = &$this->tokens;
829 if (!$this->canEdit()) {
830 if ($this->isInitialEdit()) {
831 return $this->viewSource();
833 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
834 } elseif ($this->editaction == 'save') {
835 if ($this->savePage()) {
836 return true; // Page saved.
841 if ($saveFailed || $this->isConcurrentUpdate()) {
842 // Get the text of the original page, and the two conflicting edits
843 // The diff class takes arrays as input. So retrieve content as
844 // an array, or convert it as necesary.
845 $orig = $this->page->getRevision($this->_currentVersion);
846 $this_content = explode("\n", $this->_content);
847 $other_content = $this->current->getContent();
848 require_once 'lib/diff.php';
849 $diff2 = new Diff($other_content, $this_content);
850 $context_lines = max(4, count($other_content) + 1,
851 count($this_content) + 1);
852 $fmt = new BlockDiffFormatter($context_lines);
854 $this->_content = $fmt->format($diff2);
855 // FIXME: integrate this into class BlockDiffFormatter
856 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
858 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
861 $this->_currentVersion = $this->current->getVersion();
862 $this->version = $this->_currentVersion;
863 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
866 if ($this->editaction == 'edit_convert')
867 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
868 if ($this->editaction == 'preview')
869 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
871 // FIXME: NOT_CURRENT_MESSAGE?
872 $tokens = array_merge($tokens, $this->getFormElements());
873 // we need all GET params for loadfile overwrite
874 if ($this->request->getArg('action') == 'loadfile') {
876 $this->tokens['HIDDEN_INPUTS'] =
878 (array('source' => $this->request->getArg('source'),
880 $this->tokens['HIDDEN_INPUTS']);
881 // add two conflict resolution buttons before preview and save.
882 $tokens['PREVIEW_B'] = HTML(
883 Button('submit:edit[keep_old]',
884 _("Keep old"), 'wikiaction'),
886 Button('submit:edit[overwrite]',
887 _("Overwrite with new"), 'wikiaction'),
889 $tokens['PREVIEW_B']);
891 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
892 include_once 'lib/EditToolbar.php';
893 $toolbar = new EditToolbar();
894 $tokens = array_merge($tokens, $toolbar->getTokens());
897 return $this->output('editpage', _("Merge and Edit: %s"));
900 function output($template, $title_fs)
902 $selected = &$this->selected;
903 $current = &$this->current;
905 if ($selected && $selected->getVersion() != $current->getVersion()) {
907 $pagelink = WikiLink($selected);
910 $pagelink = WikiLink($this->page);
913 $title = new FormattedText ($title_fs, $pagelink);
914 $this->tokens['HEADER'] = $title;
915 //hack! there's no TITLE in editpage, but in the previous top template
916 if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
917 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
919 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
920 $this->tokens['PAGE_LOCKED_MESSAGE']);
921 $template = Template($template, $this->tokens);
923 //GeneratePage($template, $title, $rev);
928 function getConflictMessage($unresolved = false)
930 $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.",
933 HTML::p(_("Please check it through before saving."))));
942 // c-hanging-comment-ender-p: nil
943 // indent-tabs-mode: nil