4 require_once('lib/Template.php');
8 function PageEditor (&$request) {
9 $this->request = &$request;
11 $this->user = $request->getUser();
12 $this->page = $request->getPage();
14 $this->current = $this->page->getCurrentRevision(false);
16 // HACKish short circuit to browse on action=create
17 if ($request->getArg('action') == 'create') {
18 if (! $this->current->hasDefaultContents())
19 $request->redirect(WikiURL($this->page->getName())); // noreturn
22 $this->meta = array('author' => $this->user->getId(),
23 'author_id' => $this->user->getAuthenticatedId(),
26 $this->tokens = array();
29 $backend = WYSIWYG_BACKEND;
30 // TODO: error message
31 require_once("lib/WysiwygEdit/$backend.php");
32 $class = "WysiwygEdit_$backend";
33 $this->WysiwygEdit = new $class();
36 require_once('lib/Captcha.php');
37 $this->Captcha = new Captcha($this->meta);
40 $version = $request->getArg('version');
41 if ($version !== false) {
42 $this->selected = $this->page->getRevision($version);
43 $this->version = $version;
46 $this->version = $this->current->getVersion();
47 $this->selected = $this->page->getRevision($this->version);
50 if ($this->_restoreState()) {
51 $this->_initialEdit = false;
54 $this->_initializeState();
55 $this->_initialEdit = true;
57 // The edit request has specified some initial content from a template
58 if ( ($template = $request->getArg('template'))
59 and $request->_dbi->isWikiPage($template))
61 $page = $request->_dbi->getPage($template);
62 $current = $page->getCurrentRevision();
63 $this->_content = $current->getPackedContent();
64 } elseif ($initial_content = $request->getArg('initial_content')) {
65 $this->_content = $initial_content;
66 $this->_redirect_to = $request->getArg('save_and_redirect_to');
70 header("Content-Type: text/html; charset=" . $GLOBALS['charset']);
73 function editPage () {
76 $tokens = &$this->tokens;
77 $tokens['PAGE_LOCKED_MESSAGE'] = '';
78 $tokens['CONCURRENT_UPDATE_MESSAGE'] = '';
81 if (isset($r->args['pref']['editWidth'])
82 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'])) {
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();
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));
103 elseif ( $this->savePage()) {
105 $request->setArg('action', false);
106 $r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
107 return true; // Page saved.
111 elseif ($this->editaction == 'save') {
112 if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
113 $this->tokens['PAGE_LOCKED_MESSAGE'] =
114 HTML::p(HTML::h1($this->Captcha->failed_msg));
116 elseif ($this->savePage()) {
117 return true; // Page saved.
123 // coming from loadfile conflicts
124 elseif ($this->editaction == 'keep_old') {
125 // keep old page and do nothing
126 $this->_redirectToBrowsePage();
127 //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
130 elseif ($this->editaction == 'overwrite') {
131 // take the new content without diff
132 $source = $this->request->getArg('loadfile');
133 require_once('lib/loadsave.php');
134 $this->request->setArg('loadfile', 1);
135 $this->request->setArg('overwrite', 1);
136 $this->request->setArg('merge', 0);
137 LoadFileOrDir($this->request);
138 $this->_redirectToBrowsePage();
139 //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
142 elseif ($this->editaction == 'upload') {
144 $plugin = WikiPluginLoader("UpLoad");
146 // add link to content
150 if ($saveFailed and $this->isConcurrentUpdate())
152 // Get the text of the original page, and the two conflicting edits
153 // The diff3 class takes arrays as input. So retrieve content as
154 // an array, or convert it as necesary.
155 $orig = $this->page->getRevision($this->_currentVersion);
156 // FIXME: what if _currentVersion has be deleted?
157 $orig_content = $orig->getContent();
158 $this_content = explode("\n", $this->_content);
159 $other_content = $this->current->getContent();
160 require_once("lib/diff3.php");
161 $diff = new diff3($orig_content, $this_content, $other_content);
162 $output = $diff->merged_output(_("Your version"), _("Other version"));
163 // Set the content of the textarea to the merged diff
164 // output, and update the version
165 $this->_content = implode ("\n", $output);
166 $this->_currentVersion = $this->current->getVersion();
167 $this->version = $this->_currentVersion;
168 $unresolved = $diff->ConflictingBlocks;
169 $tokens['CONCURRENT_UPDATE_MESSAGE']
170 = $this->getConflictMessage($unresolved);
171 } elseif ($saveFailed && !$this->_isSpam) {
172 $tokens['CONCURRENT_UPDATE_MESSAGE'] =
173 HTML(HTML::h2(_("Some internal editing error")),
174 HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
175 HTML::p(HTML::em(_("&version=-1 might help."))));
178 if ($this->editaction == 'edit_convert')
179 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
180 if ($this->editaction == 'preview')
181 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
182 if ($this->editaction == 'diff')
183 $tokens['PREVIEW_CONTENT'] = $this->getDiff();
185 // FIXME: NOT_CURRENT_MESSAGE?
186 $tokens = array_merge($tokens, $this->getFormElements());
188 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
189 require_once("lib/EditToolbar.php");
190 $toolbar = new EditToolbar();
191 $tokens = array_merge($tokens, $toolbar->getTokens());
194 return $this->output('editpage', _("Edit: %s"));
197 function output ($template, $title_fs) {
199 $selected = &$this->selected;
200 $current = &$this->current;
202 if ($selected && $selected->getVersion() != $current->getVersion()) {
204 $pagelink = WikiLink($selected);
208 $pagelink = WikiLink($this->page);
211 $title = new FormattedText ($title_fs, $pagelink);
212 // not for dumphtml or viewsource
213 if (ENABLE_WYSIWYG and $template == 'editpage') {
214 $WikiTheme->addMoreHeaders($this->WysiwygEdit->Head());
215 //$tokens['PAGE_SOURCE'] = $this->WysiwygEdit->ConvertBefore($this->_content);
217 $template = Template($template, $this->tokens);
218 /* Tell google (and others) not to take notice of edit links */
219 if (GOOGLE_LINKS_NOFOLLOW)
220 $args = array('ROBOTS_META' => "noindex,nofollow");
221 GeneratePage($template, $title, $rev);
226 function viewSource () {
227 assert($this->isInitialEdit());
228 assert($this->selected);
230 $this->tokens['PAGE_SOURCE'] = $this->_content;
231 $this->tokens['HIDDEN_INPUTS'] = HiddenInputs($this->request->getArgs());
232 return $this->output('viewsource', _("View Source: %s"));
235 function updateLock() {
237 if (!$this->user->isAdmin()) {
238 // FIXME: some sort of message
239 return false; // not allowed.
242 if ((bool)$this->page->get('locked') != (bool)$this->locked) {
243 $this->page->set('locked', (bool)$this->locked);
244 $this->tokens['LOCK_CHANGED_MSG']
246 ? _("Page now locked.")
247 : _("Page now unlocked.");
251 if (ENABLE_PAGE_PUBLIC) {
252 if ((bool)$this->page->get('public') != (bool)$this->public) {
253 $this->page->set('public', (bool)$this->public);
254 $this->tokens['LOCK_CHANGED_MSG']
256 ? _("Page now public.")
257 : _("Page now not-public.")) . " ";
261 return $changed; // lock changed.
264 function savePage () {
265 $request = &$this->request;
267 if ($this->isUnchanged()) {
268 // Allow admin lock/unlock even if
269 // no text changes were made.
270 if ($this->updateLock()) {
271 $dbi = $request->getDbh();
274 // Save failed. No changes made.
275 $this->_redirectToBrowsePage();
276 // user will probably not see the rest of this...
277 require_once('lib/display.php');
278 // force browse of current version:
279 $request->setArg('action', false);
280 $request->setArg('version', false);
281 displayPage($request, 'nochanges');
285 if (!$this->user->isAdmin() and $this->isSpam()) {
286 $this->_isSpam = true;
289 // Save failed. No changes made.
290 $this->_redirectToBrowsePage();
291 // user will probably not see the rest of this...
292 require_once('lib/display.php');
293 // force browse of current version:
294 $request->setArg('version', false);
295 displayPage($request, 'nochanges');
300 $page = &$this->page;
302 // Include any meta-data from original page version which
303 // has not been explicitly updated.
304 // (Except don't propagate pgsrc_version --- moot for now,
305 // because at present it never gets into the db...)
306 $meta = $this->selected->getMetaData();
307 unset($meta['pgsrc_version']);
308 $meta = array_merge($meta, $this->meta);
311 $this->_content = $this->getContent();
312 $newrevision = $page->save($this->_content,
315 : $this->_currentVersion + 1,
318 if (!isa($newrevision, 'WikiDB_PageRevision')) {
319 // Save failed. (Concurrent updates).
323 // New contents successfully saved...
326 // Clean out archived versions of this page.
327 require_once('lib/ArchiveCleaner.php');
328 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
329 $cleaner->cleanPageRevisions($page);
331 /* generate notification emails done in WikiDB::save to catch
332 all direct calls (admin plugins) */
334 // look at the errorstack
335 $errors = $GLOBALS['ErrorManager']->_postponed_errors;
336 $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
337 $GLOBALS['ErrorManager']->_postponed_errors = $errors;
339 $dbi = $request->getDbh();
343 if (empty($warnings->_content) && ! $WikiTheme->getImageURL('signature')) {
344 // Do redirect to browse page if no signature has
345 // been defined. In this case, the user will most
346 // likely not see the rest of the HTML we generate
348 $request->setArg('action', false);
349 $this->_redirectToBrowsePage();
352 // Force browse of current page version.
353 $request->setArg('version', false);
354 // testme: does preview and more need action=edit?
355 $request->setArg('action', false);
357 $template = Template('savepage', $this->tokens);
358 $template->replace('CONTENT', $newrevision->getTransformedContent());
359 if (!empty($warnings->_content)) {
360 $template->replace('WARNINGS', $warnings);
361 unset($GLOBALS['ErrorManager']->_postponed_errors);
364 $pagelink = WikiLink($page);
366 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
370 function isConcurrentUpdate () {
371 assert($this->current->getVersion() >= $this->_currentVersion);
372 return $this->current->getVersion() != $this->_currentVersion;
375 function canEdit () {
376 return !$this->page->get('locked') || $this->user->isAdmin();
379 function isInitialEdit () {
380 return $this->_initialEdit;
383 function isUnchanged () {
384 $current = &$this->current;
386 if ($this->meta['markup'] != $current->get('markup'))
389 return $this->_content == $current->getPackedContent();
393 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
394 * Need to check dynamically some blacklist wikipage settings
395 * (plugin WikiAccessRestrictions) and some static blacklist.
397 * Always: More then 20 new external links
398 * ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
399 * ENABLE_SPAMBLOCKLIST: content domain blacklist
402 $current = &$this->current;
403 $request = &$this->request;
405 $oldtext = $current->getPackedContent();
406 $newtext =& $this->_content;
407 $numlinks = $this->numLinks($newtext);
408 $newlinks = $numlinks - $this->numLinks($oldtext);
409 // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
410 // better use a certain text : link ratio.
412 // 1. Not more then 20 new external links
413 if ($newlinks >= NUM_SPAM_LINKS)
415 // Allow strictly authenticated users?
416 // TODO: mail the admin?
417 $this->tokens['PAGE_LOCKED_MESSAGE'] =
418 HTML($this->getSpamMessage(),
419 HTML::p(HTML::strong(_("Too many external links."))));
422 // 2. external babycart (SpamAssassin) check
423 // This will probably prevent from discussing sex or viagra related topics. So beware.
424 if (ENABLE_SPAMASSASSIN) {
425 require_once("lib/spam_babycart.php");
426 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
427 $this->user->getId())) {
428 // TODO: mail the admin
429 if (is_array($babycart))
430 $this->tokens['PAGE_LOCKED_MESSAGE'] =
431 HTML($this->getSpamMessage(),
432 HTML::p(HTML::em(_("SpamAssassin reports: "),
433 join("\n", $babycart))));
437 // 3. extract (new) links and check surbl for blocked domains
438 if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
439 require_once("lib/SpamBlocklist.php");
440 require_once("lib/InlineParser.php");
441 $parsed = TransformLinks($newtext);
442 $oldparsed = TransformLinks($oldtext);
444 foreach ($oldparsed->_content as $link) {
445 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
446 $uri = $link->_getURL($this->page->getName());
451 foreach ($parsed->_content as $link) {
452 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
453 $uri = $link->_getURL($this->page->getName());
454 // only check new links, so admins may add blocked links.
455 if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
456 // TODO: mail the admin
457 $this->tokens['PAGE_LOCKED_MESSAGE'] =
458 HTML($this->getSpamMessage(),
459 HTML::p(HTML::strong(_("External links contain blocked domains:")),
460 HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
461 $uri." [".$res[2]."]", $res[0], $res[1])))));
474 /** Number of external links in the wikitext
476 function numLinks(&$text) {
477 return substr_count($text, "http://") + substr_count($text, "https://");
480 /** Header of the Anti Spam message
482 function getSpamMessage () {
484 HTML(HTML::h2(_("Spam Prevention")),
485 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
487 _("Sorry for the inconvenience.")),
491 function getPreview () {
492 require_once('lib/PageType.php');
493 $this->_content = $this->getContent();
494 return new TransformedText($this->page, $this->_content, $this->meta);
497 function getConvertedPreview () {
498 require_once('lib/PageType.php');
499 $this->_content = $this->getContent();
500 $this->meta['markup'] = 2.0;
501 $this->_content = ConvertOldMarkup($this->_content);
502 return new TransformedText($this->page, $this->_content, $this->meta);
505 function getDiff () {
506 require_once('lib/diff.php');
509 $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
510 if ($diff->isEmpty()) {
511 $html->pushContent(HTML::hr(),
512 HTML::p('[', _("Versions are identical"),
516 // New CSS formatted unified diffs
517 $fmt = new HtmlUnifiedDiffFormatter;
518 // Use this for old table-formatted diffs.
519 //$fmt = new TableUnifiedDiffFormatter;
520 $html->pushContent($fmt->format($diff));
525 // possibly convert HTMLAREA content back to Wiki markup
526 function getContent () {
527 if (ENABLE_WYSIWYG) {
528 // don't store everything as html
529 if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
530 // Wikiwyg shortcut to avoid the InlineTransformer:
531 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
532 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
533 $this->_content = join("", $xml_output->_content);
535 $this->meta['pagetype'] = 'html';
537 return $this->_content;
539 return $this->_content;
543 function getLockedMessage () {
545 HTML(HTML::h2(_("Page Locked")),
546 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
547 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
548 HTML::p(_("Sorry for the inconvenience.")));
551 function isModerated() {
552 return $this->page->get('moderation');
554 function getModeratedMessage() {
556 HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
557 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")))),
558 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.",
559 WikiLink(_("UserPreferences")))));
561 function getConflictMessage ($unresolved = false) {
563 xgettext only knows about c/c++ line-continuation strings
564 it does not know about php's dot operator.
565 We want to translate this entire paragraph as one string, of course.
568 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
571 $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.",
572 "<<<<<<< ". _("Your version"),
573 ">>>>>>> ". _("Other version")));
575 $message = HTML::p(_("Please check it through before saving."));
579 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
580 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
582 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
583 HTML::li(_("Save your updated changes.")));
586 HTML(HTML::h2(_("Conflicting Edits!")),
587 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
588 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.")),
593 function getTextArea () {
594 $request = &$this->request;
596 $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
598 // WYSIWYG will need two pagetypes: raw wikitest and converted html
599 if (ENABLE_WYSIWYG) {
600 $this->_wikicontent = $this->_content;
601 $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
602 // $this->getPreview();
603 //$this->_htmlcontent = $this->_content->asXML();
606 $textarea = HTML::textarea(array('class'=> 'wikiedit',
607 'name' => 'edit[content]',
608 'id' => 'edit-content',
609 'rows' => $request->getPref('editHeight'),
610 'cols' => $request->getPref('editWidth'),
611 'readonly' => (bool) $readonly),
613 if (ENABLE_WYSIWYG) {
614 return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
615 $textarea->getAttr('name'));
620 function getFormElements () {
622 $request = &$this->request;
623 $page = &$this->page;
625 $h = array('action' => 'edit',
626 'pagename' => $page->getName(),
627 'version' => $this->version,
628 'edit[pagetype]' => $this->meta['pagetype'],
629 'edit[current_version]' => $this->_currentVersion);
631 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
632 $el['EDIT_TEXTAREA'] = $this->getTextArea();
633 if ( ENABLE_CAPTCHA ) {
634 $el = array_merge($el, $this->Captcha->getFormElements());
637 = HTML::input(array('type' => 'text',
638 'class' => 'wikitext',
639 'id' => 'edit-summary',
640 'name' => 'edit[summary]',
643 'value' => $this->meta['summary']));
645 = HTML::input(array('type' => 'checkbox',
646 'name' => 'edit[minor_edit]',
647 'id' => 'edit-minor_edit',
648 'checked' => (bool) $this->meta['is_minor_edit']));
650 = HTML::input(array('type' => 'checkbox',
651 'name' => 'edit[markup]',
653 'checked' => $this->meta['markup'] < 2.0,
654 'id' => 'useOldMarkup',
655 'onclick' => 'showOldMarkupRules(this.checked)'));
656 $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0)
657 ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
659 = HTML::input(array('type' => 'checkbox',
660 'name' => 'edit[locked]',
661 'id' => 'edit-locked',
662 'disabled' => (bool) !$this->user->isadmin(),
663 'checked' => (bool) $this->locked));
665 if (ENABLE_PAGE_PUBLIC) {
667 = HTML::input(array('type' => 'checkbox',
668 'name' => 'edit[public]',
669 'id' => 'edit-public',
670 'disabled' => (bool) !$this->user->isAdmin(),
671 'checked' => (bool) $this->page->get('public')));
673 if (ENABLE_WYSIWYG) {
674 if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
675 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
679 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
681 array('accesskey'=> 'p',
682 'title' => 'Preview the current content [alt-p]'));
684 //if (!$this->isConcurrentUpdate() && $this->canEdit())
685 $el['SAVE_B'] = Button('submit:edit[save]',
686 _("Save"), 'wikiaction',
687 array('accesskey'=> 's',
688 'title' => 'Save the current content as wikipage [alt-s]'));
689 $el['CHANGES_B'] = Button('submit:edit[diff]',
690 _("Changes"), 'wikiaction',
691 array('accesskey'=> 'c',
692 'title' => 'Preview the current changes as diff [alt-c]'));
693 $el['UPLOAD_B'] = Button('submit:edit[upload]',
694 _("Upload"), 'wikiaction',
695 array('title' => 'Select a local file and press Upload to attach into this page'));
696 $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
697 _("Spell Check"), 'wikiaction',
698 array('title' => 'Check the spelling'));
699 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
702 = HTML::input(array('type' => 'text',
705 'class' => "numeric",
706 'name' => 'pref[editWidth]',
707 'id' => 'pref-editWidth',
708 'value' => $request->getPref('editWidth'),
709 'onchange' => 'this.form.submit();'));
711 = HTML::input(array('type' => 'text',
714 'class' => "numeric",
715 'name' => 'pref[editHeight]',
716 'id' => 'pref-editHeight',
717 'value' => $request->getPref('editHeight'),
718 'onchange' => 'this.form.submit();'));
719 $el['SEP'] = $WikiTheme->getButtonSeparator();
720 $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
721 HTML::em($this->user->getId()));
726 function _redirectToBrowsePage() {
727 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
730 function _restoreState () {
731 $request = &$this->request;
733 $posted = $request->getArg('edit');
734 $request->setArg('edit', false);
737 || !$request->isPost()
738 || !in_array($request->getArg('action'),array('edit','loadfile')))
741 if (!isset($posted['content']) || !is_string($posted['content']))
743 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
744 rtrim($posted['content']));
745 $this->_content = $this->getContent();
747 $this->_currentVersion = (int) $posted['current_version'];
749 if ($this->_currentVersion < 0)
751 if ($this->_currentVersion > $this->current->getVersion())
752 return false; // FIXME: some kind of warning?
754 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
755 $meta['markup'] = $is_old_markup ? false : 2.0;
756 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
757 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
758 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
759 if ( ENABLE_CAPTCHA )
760 $meta['captcha_input'] = !empty($posted['captcha_input']) ?
761 $posted['captcha_input'] : '';
763 $this->meta = array_merge($this->meta, $meta);
764 $this->locked = !empty($posted['locked']);
765 if (ENABLE_PAGE_PUBLIC)
766 $this->public = !empty($posted['public']);
768 foreach (array('preview','save','edit_convert',
769 'keep_old','overwrite','diff','upload') as $o)
771 if (!empty($posted[$o]))
772 $this->editaction = $o;
774 if (empty($this->editaction))
775 $this->editaction = 'edit';
780 function _initializeState () {
781 $request = &$this->request;
782 $current = &$this->current;
783 $selected = &$this->selected;
784 $user = &$this->user;
787 NoSuchRevision($request, $this->page, $this->version); // noreturn
789 $this->_currentVersion = $current->getVersion();
790 $this->_content = $selected->getPackedContent();
792 $this->locked = $this->page->get('locked');
794 // If author same as previous author, default minor_edit to on.
795 $age = $this->meta['mtime'] - $current->get('mtime');
796 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
797 && $current->get('author') == $user->getId()
800 // Default for new pages is new-style markup.
801 if ($selected->hasDefaultContents())
802 $is_new_markup = true;
804 $is_new_markup = $selected->get('markup') >= 2.0;
806 $this->meta['markup'] = $is_new_markup ? 2.0: false;
807 $this->meta['pagetype'] = $selected->get('pagetype');
808 if ($this->meta['pagetype'] == 'wikiblog')
809 $this->meta['summary'] = $selected->get('summary'); // keep blog title
811 $this->meta['summary'] = '';
812 $this->editaction = 'edit';
816 class LoadFileConflictPageEditor
819 function editPage ($saveFailed = true) {
820 $tokens = &$this->tokens;
822 if (!$this->canEdit()) {
823 if ($this->isInitialEdit()) {
824 return $this->viewSource();
826 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
828 elseif ($this->editaction == 'save') {
829 if ($this->savePage()) {
830 return true; // Page saved.
835 if ($saveFailed || $this->isConcurrentUpdate())
837 // Get the text of the original page, and the two conflicting edits
838 // The diff class takes arrays as input. So retrieve content as
839 // an array, or convert it as necesary.
840 $orig = $this->page->getRevision($this->_currentVersion);
841 $this_content = explode("\n", $this->_content);
842 $other_content = $this->current->getContent();
843 require_once("lib/diff.php");
844 $diff2 = new Diff($other_content, $this_content);
845 $context_lines = max(4, count($other_content) + 1,
846 count($this_content) + 1);
847 $fmt = new BlockDiffFormatter($context_lines);
849 $this->_content = $fmt->format($diff2);
850 // FIXME: integrate this into class BlockDiffFormatter
851 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
853 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
856 $this->_currentVersion = $this->current->getVersion();
857 $this->version = $this->_currentVersion;
858 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
861 if ($this->editaction == 'edit_convert')
862 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
863 if ($this->editaction == 'preview')
864 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
866 // FIXME: NOT_CURRENT_MESSAGE?
867 $tokens = array_merge($tokens, $this->getFormElements());
868 // we need all GET params for loadfile overwrite
869 if ($this->request->getArg('action') == 'loadfile') {
871 $this->tokens['HIDDEN_INPUTS'] =
873 (array('source' => $this->request->getArg('source'),
875 $this->tokens['HIDDEN_INPUTS']);
876 // add two conflict resolution buttons before preview and save.
877 $tokens['PREVIEW_B'] = HTML(
878 Button('submit:edit[keep_old]',
879 _("Keep old"), 'wikiaction'),
881 Button('submit:edit[overwrite]',
882 _("Overwrite with new"), 'wikiaction'),
884 $tokens['PREVIEW_B']);
886 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
887 include_once("lib/EditToolbar.php");
888 $toolbar = new EditToolbar();
889 $tokens = array_merge($tokens, $toolbar->getTokens());
892 return $this->output('editpage', _("Merge and Edit: %s"));
895 function output ($template, $title_fs) {
896 $selected = &$this->selected;
897 $current = &$this->current;
899 if ($selected && $selected->getVersion() != $current->getVersion()) {
901 $pagelink = WikiLink($selected);
905 $pagelink = WikiLink($this->page);
908 $title = new FormattedText ($title_fs, $pagelink);
909 $this->tokens['HEADER'] = $title;
910 //hack! there's no TITLE in editpage, but in the previous top template
911 if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
912 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
914 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
915 $this->tokens['PAGE_LOCKED_MESSAGE']);
916 $template = Template($template, $this->tokens);
918 //GeneratePage($template, $title, $rev);
923 function getConflictMessage () {
924 $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.",
927 HTML::p(_("Please check it through before saving."))));
936 // c-hanging-comment-ender-p: nil
937 // indent-tabs-mode: nil