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['LOCK_CHANGED_MSG'] = '';
79 $tokens['CONCURRENT_UPDATE_MESSAGE'] = '';
82 if (isset($r->args['pref']['editWidth'])
83 and ($r->getPref('editWidth') != $r->args['pref']['editWidth'])) {
84 $r->_prefs->set('editWidth', $r->args['pref']['editWidth']);
86 if (isset($r->args['pref']['editHeight'])
87 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();
99 elseif ($r->getArg('save_and_redirect_to') != "") {
100 if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
101 $this->tokens['PAGE_LOCKED_MESSAGE'] =
102 HTML::p(HTML::h1($this->Captcha->failed_msg));
104 elseif ( $this->savePage()) {
106 $request->setArg('action', false);
107 $r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
108 return true; // Page saved.
112 elseif ($this->editaction == 'save') {
113 if (ENABLE_CAPTCHA && $this->Captcha->Failed()) {
114 $this->tokens['PAGE_LOCKED_MESSAGE'] =
115 HTML::p(HTML::h1($this->Captcha->failed_msg));
117 elseif ($this->savePage()) {
118 return true; // Page saved.
124 // coming from loadfile conflicts
125 elseif ($this->editaction == 'keep_old') {
126 // keep old page and do nothing
127 $this->_redirectToBrowsePage();
128 //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
131 elseif ($this->editaction == 'overwrite') {
132 // take the new content without diff
133 $source = $this->request->getArg('loadfile');
134 require_once('lib/loadsave.php');
135 $this->request->setArg('loadfile', 1);
136 $this->request->setArg('overwrite', 1);
137 $this->request->setArg('merge', 0);
138 LoadFileOrDir($this->request);
139 $this->_redirectToBrowsePage();
140 //$r->redirect(WikiURL($r->getArg('save_and_redirect_to')));
143 elseif ($this->editaction == 'upload') {
145 $plugin = WikiPluginLoader("UpLoad");
147 // add link to content
151 if ($saveFailed and $this->isConcurrentUpdate())
153 // Get the text of the original page, and the two conflicting edits
154 // The diff3 class takes arrays as input. So retrieve content as
155 // an array, or convert it as necesary.
156 $orig = $this->page->getRevision($this->_currentVersion);
157 // FIXME: what if _currentVersion has be deleted?
158 $orig_content = $orig->getContent();
159 $this_content = explode("\n", $this->_content);
160 $other_content = $this->current->getContent();
161 require_once("lib/diff3.php");
162 $diff = new diff3($orig_content, $this_content, $other_content);
163 $output = $diff->merged_output(_("Your version"), _("Other version"));
164 // Set the content of the textarea to the merged diff
165 // output, and update the version
166 $this->_content = implode ("\n", $output);
167 $this->_currentVersion = $this->current->getVersion();
168 $this->version = $this->_currentVersion;
169 $unresolved = $diff->ConflictingBlocks;
170 $tokens['CONCURRENT_UPDATE_MESSAGE']
171 = $this->getConflictMessage($unresolved);
172 } elseif ($saveFailed && !$this->_isSpam) {
173 $tokens['CONCURRENT_UPDATE_MESSAGE'] =
174 HTML(HTML::h2(_("Some internal editing error")),
175 HTML::p(_("Your are probably trying to edit/create an invalid version of this page.")),
176 HTML::p(HTML::em(_("&version=-1 might help."))));
179 if ($this->editaction == 'edit_convert')
180 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
181 if ($this->editaction == 'preview')
182 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
183 if ($this->editaction == 'diff')
184 $tokens['PREVIEW_CONTENT'] = $this->getDiff();
186 // FIXME: NOT_CURRENT_MESSAGE?
187 $tokens = array_merge($tokens, $this->getFormElements());
189 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
190 require_once("lib/EditToolbar.php");
191 $toolbar = new EditToolbar();
192 $tokens = array_merge($tokens, $toolbar->getTokens());
195 return $this->output('editpage', _("Edit: %s"));
198 function output ($template, $title_fs) {
200 $selected = &$this->selected;
201 $current = &$this->current;
203 if ($selected && $selected->getVersion() != $current->getVersion()) {
205 $pagelink = WikiLink($selected);
209 $pagelink = WikiLink($this->page);
212 $title = new FormattedText ($title_fs, $pagelink);
213 // not for dumphtml or viewsource
214 if (ENABLE_WYSIWYG and $template == 'editpage') {
215 $WikiTheme->addMoreHeaders($this->WysiwygEdit->Head());
216 //$tokens['PAGE_SOURCE'] = $this->WysiwygEdit->ConvertBefore($this->_content);
218 $template = Template($template, $this->tokens);
219 /* Tell google (and others) not to take notice of edit links */
220 if (GOOGLE_LINKS_NOFOLLOW)
221 $args = array('ROBOTS_META' => "noindex,nofollow");
222 GeneratePage($template, $title, $rev);
227 function viewSource () {
228 assert($this->isInitialEdit());
229 assert($this->selected);
231 $this->tokens['PAGE_SOURCE'] = $this->_content;
232 $this->tokens['HIDDEN_INPUTS'] = HiddenInputs($this->request->getArgs());
233 return $this->output('viewsource', _("View Source: %s"));
236 function updateLock() {
238 if (!$this->user->isAdmin()) {
239 // FIXME: some sort of message
240 return false; // not allowed.
243 if ((bool)$this->page->get('locked') != (bool)$this->locked) {
244 $this->page->set('locked', (bool)$this->locked);
245 $this->tokens['LOCK_CHANGED_MSG']
247 ? _("Page now locked.")
248 : _("Page now unlocked.");
252 if (ENABLE_PAGE_PUBLIC) {
253 if ((bool)$this->page->get('public') != (bool)$this->public) {
254 $this->page->set('public', (bool)$this->public);
255 $this->tokens['LOCK_CHANGED_MSG']
257 ? _("Page now public.")
258 : _("Page now not-public.")) . " ";
262 return $changed; // lock changed.
265 function savePage () {
266 $request = &$this->request;
268 if ($this->isUnchanged()) {
269 // Allow admin lock/unlock even if
270 // no text changes were made.
271 if ($this->updateLock()) {
272 $dbi = $request->getDbh();
275 // Save failed. No changes made.
276 $this->_redirectToBrowsePage();
277 // user will probably not see the rest of this...
278 require_once('lib/display.php');
279 // force browse of current version:
280 $request->setArg('action', false);
281 $request->setArg('version', false);
282 displayPage($request, 'nochanges');
286 if (!$this->user->isAdmin() and $this->isSpam()) {
287 $this->_isSpam = true;
290 // Save failed. No changes made.
291 $this->_redirectToBrowsePage();
292 // user will probably not see the rest of this...
293 require_once('lib/display.php');
294 // force browse of current version:
295 $request->setArg('version', false);
296 displayPage($request, 'nochanges');
301 $page = &$this->page;
303 // Include any meta-data from original page version which
304 // has not been explicitly updated.
305 // (Except don't propagate pgsrc_version --- moot for now,
306 // because at present it never gets into the db...)
307 $meta = $this->selected->getMetaData();
308 unset($meta['pgsrc_version']);
309 $meta = array_merge($meta, $this->meta);
312 $this->_content = $this->getContent();
313 $newrevision = $page->save($this->_content,
316 : $this->_currentVersion + 1,
319 if (!isa($newrevision, 'WikiDB_PageRevision')) {
320 // Save failed. (Concurrent updates).
324 // New contents successfully saved...
327 // Clean out archived versions of this page.
328 require_once('lib/ArchiveCleaner.php');
329 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
330 $cleaner->cleanPageRevisions($page);
332 /* generate notification emails done in WikiDB::save to catch
333 all direct calls (admin plugins) */
335 // look at the errorstack
336 $errors = $GLOBALS['ErrorManager']->_postponed_errors;
337 $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
338 $GLOBALS['ErrorManager']->_postponed_errors = $errors;
340 $dbi = $request->getDbh();
344 if (empty($warnings->_content) && ! $WikiTheme->getImageURL('signature')) {
345 // Do redirect to browse page if no signature has
346 // been defined. In this case, the user will most
347 // likely not see the rest of the HTML we generate
349 $request->setArg('action', false);
350 $this->_redirectToBrowsePage();
353 // Force browse of current page version.
354 $request->setArg('version', false);
355 // testme: does preview and more need action=edit?
356 $request->setArg('action', false);
358 $template = Template('savepage', $this->tokens);
359 $template->replace('CONTENT', $newrevision->getTransformedContent());
360 if (!empty($warnings->_content)) {
361 $template->replace('WARNINGS', $warnings);
362 unset($GLOBALS['ErrorManager']->_postponed_errors);
365 $pagelink = WikiLink($page);
367 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
371 function isConcurrentUpdate () {
372 assert($this->current->getVersion() >= $this->_currentVersion);
373 return $this->current->getVersion() != $this->_currentVersion;
376 function canEdit () {
377 return !$this->page->get('locked') || $this->user->isAdmin();
380 function isInitialEdit () {
381 return $this->_initialEdit;
384 function isUnchanged () {
385 $current = &$this->current;
387 if ($this->meta['markup'] != $current->get('markup'))
390 return $this->_content == $current->getPackedContent();
394 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
395 * Need to check dynamically some blacklist wikipage settings
396 * (plugin WikiAccessRestrictions) and some static blacklist.
398 * Always: More then 20 new external links
399 * ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
400 * ENABLE_SPAMBLOCKLIST: content domain blacklist
403 $current = &$this->current;
404 $request = &$this->request;
406 $oldtext = $current->getPackedContent();
407 $newtext =& $this->_content;
408 $numlinks = $this->numLinks($newtext);
409 $newlinks = $numlinks - $this->numLinks($oldtext);
410 // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
411 // better use a certain text : link ratio.
413 // 1. Not more then 20 new external links
414 if ($newlinks >= NUM_SPAM_LINKS)
416 // Allow strictly authenticated users?
417 // TODO: mail the admin?
418 $this->tokens['PAGE_LOCKED_MESSAGE'] =
419 HTML($this->getSpamMessage(),
420 HTML::p(HTML::strong(_("Too many external links."))));
423 // 2. external babycart (SpamAssassin) check
424 // This will probably prevent from discussing sex or viagra related topics. So beware.
425 if (ENABLE_SPAMASSASSIN) {
426 require_once("lib/spam_babycart.php");
427 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
428 $this->user->getId())) {
429 // TODO: mail the admin
430 if (is_array($babycart))
431 $this->tokens['PAGE_LOCKED_MESSAGE'] =
432 HTML($this->getSpamMessage(),
433 HTML::p(HTML::em(_("SpamAssassin reports: "),
434 join("\n", $babycart))));
438 // 3. extract (new) links and check surbl for blocked domains
439 if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
440 require_once("lib/SpamBlocklist.php");
441 require_once("lib/InlineParser.php");
442 $parsed = TransformLinks($newtext);
443 $oldparsed = TransformLinks($oldtext);
445 foreach ($oldparsed->_content as $link) {
446 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
447 $uri = $link->_getURL($this->page->getName());
452 foreach ($parsed->_content as $link) {
453 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
454 $uri = $link->_getURL($this->page->getName());
455 // only check new links, so admins may add blocked links.
456 if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
457 // TODO: mail the admin
458 $this->tokens['PAGE_LOCKED_MESSAGE'] =
459 HTML($this->getSpamMessage(),
460 HTML::p(HTML::strong(_("External links contain blocked domains:")),
461 HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
462 $uri." [".$res[2]."]", $res[0], $res[1])))));
475 /** Number of external links in the wikitext
477 function numLinks(&$text) {
478 return substr_count($text, "http://") + substr_count($text, "https://");
481 /** Header of the Anti Spam message
483 function getSpamMessage () {
485 HTML(HTML::h2(_("Spam Prevention")),
486 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
488 _("Sorry for the inconvenience.")),
492 function getPreview () {
493 require_once('lib/PageType.php');
494 $this->_content = $this->getContent();
495 return new TransformedText($this->page, $this->_content, $this->meta);
498 function getConvertedPreview () {
499 require_once('lib/PageType.php');
500 $this->_content = $this->getContent();
501 $this->meta['markup'] = 2.0;
502 $this->_content = ConvertOldMarkup($this->_content);
503 return new TransformedText($this->page, $this->_content, $this->meta);
506 function getDiff () {
507 require_once('lib/diff.php');
510 $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
511 if ($diff->isEmpty()) {
512 $html->pushContent(HTML::hr(),
513 HTML::p('[', _("Versions are identical"),
517 // New CSS formatted unified diffs
518 $fmt = new HtmlUnifiedDiffFormatter;
519 // Use this for old table-formatted diffs.
520 //$fmt = new TableUnifiedDiffFormatter;
521 $html->pushContent($fmt->format($diff));
526 // possibly convert HTMLAREA content back to Wiki markup
527 function getContent () {
528 if (ENABLE_WYSIWYG) {
529 // don't store everything as html
530 if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
531 // Wikiwyg shortcut to avoid the InlineTransformer:
532 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
533 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
534 $this->_content = join("", $xml_output->_content);
536 $this->meta['pagetype'] = 'html';
538 return $this->_content;
540 return $this->_content;
544 function getLockedMessage () {
546 HTML(HTML::h2(_("Page Locked")),
547 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
548 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
549 HTML::p(_("Sorry for the inconvenience.")));
552 function isModerated() {
553 return $this->page->get('moderation');
555 function getModeratedMessage() {
557 HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
558 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")))),
559 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.",
560 WikiLink(_("UserPreferences")))));
562 function getConflictMessage ($unresolved = false) {
564 xgettext only knows about c/c++ line-continuation strings
565 it does not know about php's dot operator.
566 We want to translate this entire paragraph as one string, of course.
569 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
572 $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.",
573 "<<<<<<< ". _("Your version"),
574 ">>>>>>> ". _("Other version")));
576 $message = HTML::p(_("Please check it through before saving."));
580 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
581 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
583 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
584 HTML::li(_("Save your updated changes.")));
587 HTML(HTML::h2(_("Conflicting Edits!")),
588 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
589 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.")),
594 function getTextArea () {
595 $request = &$this->request;
597 $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
599 // WYSIWYG will need two pagetypes: raw wikitest and converted html
600 if (ENABLE_WYSIWYG) {
601 $this->_wikicontent = $this->_content;
602 $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
603 // $this->getPreview();
604 //$this->_htmlcontent = $this->_content->asXML();
607 $textarea = HTML::textarea(array('class'=> 'wikiedit',
608 'name' => 'edit[content]',
609 'id' => 'edit-content',
610 'rows' => $request->getPref('editHeight'),
611 'cols' => $request->getPref('editWidth'),
612 'readonly' => (bool) $readonly),
614 if (ENABLE_WYSIWYG) {
615 return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
616 $textarea->getAttr('name'));
621 function getFormElements () {
623 $request = &$this->request;
624 $page = &$this->page;
626 $h = array('action' => 'edit',
627 'pagename' => $page->getName(),
628 'version' => $this->version,
629 'edit[pagetype]' => $this->meta['pagetype'],
630 'edit[current_version]' => $this->_currentVersion);
632 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
633 $el['EDIT_TEXTAREA'] = $this->getTextArea();
634 if ( ENABLE_CAPTCHA ) {
635 $el = array_merge($el, $this->Captcha->getFormElements());
638 = HTML::input(array('type' => 'text',
639 'class' => 'wikitext',
640 'id' => 'edit-summary',
641 'name' => 'edit[summary]',
644 'value' => $this->meta['summary']));
646 = HTML::input(array('type' => 'checkbox',
647 'name' => 'edit[minor_edit]',
648 'id' => 'edit-minor_edit',
649 'checked' => (bool) $this->meta['is_minor_edit']));
651 = HTML::input(array('type' => 'checkbox',
652 'name' => 'edit[markup]',
654 'checked' => $this->meta['markup'] < 2.0,
655 'id' => 'useOldMarkup',
656 'onclick' => 'showOldMarkupRules(this.checked)'));
657 $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0)
658 ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
660 = HTML::input(array('type' => 'checkbox',
661 'name' => 'edit[locked]',
662 'id' => 'edit-locked',
663 'disabled' => (bool) !$this->user->isadmin(),
664 'checked' => (bool) $this->locked));
666 if (ENABLE_PAGE_PUBLIC) {
668 = HTML::input(array('type' => 'checkbox',
669 'name' => 'edit[public]',
670 'id' => 'edit-public',
671 'disabled' => (bool) !$this->user->isAdmin(),
672 'checked' => (bool) $this->page->get('public')));
674 if (ENABLE_WYSIWYG) {
675 if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
676 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
680 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
682 array('accesskey'=> 'p',
683 'title' => 'Preview the current content [alt-p]'));
685 //if (!$this->isConcurrentUpdate() && $this->canEdit())
686 $el['SAVE_B'] = Button('submit:edit[save]',
687 _("Save"), 'wikiaction',
688 array('accesskey'=> 's',
689 'title' => 'Save the current content as wikipage [alt-s]'));
690 $el['CHANGES_B'] = Button('submit:edit[diff]',
691 _("Changes"), 'wikiaction',
692 array('accesskey'=> 'c',
693 'title' => 'Preview the current changes as diff [alt-c]'));
694 $el['UPLOAD_B'] = Button('submit:edit[upload]',
695 _("Upload"), 'wikiaction',
696 array('title' => 'Select a local file and press Upload to attach into this page'));
697 $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
698 _("Spell Check"), 'wikiaction',
699 array('title' => 'Check the spelling'));
700 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
703 = HTML::input(array('type' => 'text',
706 'class' => "numeric",
707 'name' => 'pref[editWidth]',
708 'id' => 'pref-editWidth',
709 'value' => $request->getPref('editWidth'),
710 'onchange' => 'this.form.submit();'));
712 = HTML::input(array('type' => 'text',
715 'class' => "numeric",
716 'name' => 'pref[editHeight]',
717 'id' => 'pref-editHeight',
718 'value' => $request->getPref('editHeight'),
719 'onchange' => 'this.form.submit();'));
720 $el['SEP'] = $WikiTheme->getButtonSeparator();
721 $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
722 HTML::em($this->user->getId()));
727 function _redirectToBrowsePage() {
728 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
731 function _restoreState () {
732 $request = &$this->request;
734 $posted = $request->getArg('edit');
735 $request->setArg('edit', false);
738 || !$request->isPost()
739 || !in_array($request->getArg('action'),array('edit','loadfile')))
742 if (!isset($posted['content']) || !is_string($posted['content']))
744 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
745 rtrim($posted['content']));
746 $this->_content = $this->getContent();
748 $this->_currentVersion = (int) $posted['current_version'];
750 if ($this->_currentVersion < 0)
752 if ($this->_currentVersion > $this->current->getVersion())
753 return false; // FIXME: some kind of warning?
755 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
756 $meta['markup'] = $is_old_markup ? false : 2.0;
757 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
758 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
759 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
760 if ( ENABLE_CAPTCHA )
761 $meta['captcha_input'] = !empty($posted['captcha_input']) ?
762 $posted['captcha_input'] : '';
764 $this->meta = array_merge($this->meta, $meta);
765 $this->locked = !empty($posted['locked']);
766 if (ENABLE_PAGE_PUBLIC)
767 $this->public = !empty($posted['public']);
769 foreach (array('preview','save','edit_convert',
770 'keep_old','overwrite','diff','upload') as $o)
772 if (!empty($posted[$o]))
773 $this->editaction = $o;
775 if (empty($this->editaction))
776 $this->editaction = 'edit';
781 function _initializeState () {
782 $request = &$this->request;
783 $current = &$this->current;
784 $selected = &$this->selected;
785 $user = &$this->user;
788 NoSuchRevision($request, $this->page, $this->version); // noreturn
790 $this->_currentVersion = $current->getVersion();
791 $this->_content = $selected->getPackedContent();
793 $this->locked = $this->page->get('locked');
795 // If author same as previous author, default minor_edit to on.
796 $age = $this->meta['mtime'] - $current->get('mtime');
797 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
798 && $current->get('author') == $user->getId()
801 // Default for new pages is new-style markup.
802 if ($selected->hasDefaultContents())
803 $is_new_markup = true;
805 $is_new_markup = $selected->get('markup') >= 2.0;
807 $this->meta['markup'] = $is_new_markup ? 2.0: false;
808 $this->meta['pagetype'] = $selected->get('pagetype');
809 if ($this->meta['pagetype'] == 'wikiblog')
810 $this->meta['summary'] = $selected->get('summary'); // keep blog title
812 $this->meta['summary'] = '';
813 $this->editaction = 'edit';
817 class LoadFileConflictPageEditor
820 function editPage ($saveFailed = true) {
821 $tokens = &$this->tokens;
823 if (!$this->canEdit()) {
824 if ($this->isInitialEdit()) {
825 return $this->viewSource();
827 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
829 elseif ($this->editaction == 'save') {
830 if ($this->savePage()) {
831 return true; // Page saved.
836 if ($saveFailed || $this->isConcurrentUpdate())
838 // Get the text of the original page, and the two conflicting edits
839 // The diff class takes arrays as input. So retrieve content as
840 // an array, or convert it as necesary.
841 $orig = $this->page->getRevision($this->_currentVersion);
842 $this_content = explode("\n", $this->_content);
843 $other_content = $this->current->getContent();
844 require_once("lib/diff.php");
845 $diff2 = new Diff($other_content, $this_content);
846 $context_lines = max(4, count($other_content) + 1,
847 count($this_content) + 1);
848 $fmt = new BlockDiffFormatter($context_lines);
850 $this->_content = $fmt->format($diff2);
851 // FIXME: integrate this into class BlockDiffFormatter
852 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
854 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
857 $this->_currentVersion = $this->current->getVersion();
858 $this->version = $this->_currentVersion;
859 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
862 if ($this->editaction == 'edit_convert')
863 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
864 if ($this->editaction == 'preview')
865 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
867 // FIXME: NOT_CURRENT_MESSAGE?
868 $tokens = array_merge($tokens, $this->getFormElements());
869 // we need all GET params for loadfile overwrite
870 if ($this->request->getArg('action') == 'loadfile') {
872 $this->tokens['HIDDEN_INPUTS'] =
874 (array('source' => $this->request->getArg('source'),
876 $this->tokens['HIDDEN_INPUTS']);
877 // add two conflict resolution buttons before preview and save.
878 $tokens['PREVIEW_B'] = HTML(
879 Button('submit:edit[keep_old]',
880 _("Keep old"), 'wikiaction'),
882 Button('submit:edit[overwrite]',
883 _("Overwrite with new"), 'wikiaction'),
885 $tokens['PREVIEW_B']);
887 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
888 include_once("lib/EditToolbar.php");
889 $toolbar = new EditToolbar();
890 $tokens = array_merge($tokens, $toolbar->getTokens());
893 return $this->output('editpage', _("Merge and Edit: %s"));
896 function output ($template, $title_fs) {
897 $selected = &$this->selected;
898 $current = &$this->current;
900 if ($selected && $selected->getVersion() != $current->getVersion()) {
902 $pagelink = WikiLink($selected);
906 $pagelink = WikiLink($this->page);
909 $title = new FormattedText ($title_fs, $pagelink);
910 $this->tokens['HEADER'] = $title;
911 //hack! there's no TITLE in editpage, but in the previous top template
912 if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
913 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
915 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
916 $this->tokens['PAGE_LOCKED_MESSAGE']);
917 $template = Template($template, $this->tokens);
919 //GeneratePage($template, $title, $rev);
924 function getConflictMessage () {
925 $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.",
928 HTML::p(_("Please check it through before saving."))));
937 // c-hanging-comment-ender-p: nil
938 // indent-tabs-mode: nil