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 (!ENABLE_PAGE_PUBLIC) {
238 if ((bool)$this->page->get('locked') == (bool)$this->locked)
239 return false; // Not changed.
242 if (!$this->user->isAdmin()) {
243 // FIXME: some sort of message
244 return false; // not allowed.
247 if (ENABLE_PAGE_PUBLIC) {
248 if ((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 $this->page->set('locked', (bool)$this->locked);
258 $this->tokens['LOCK_CHANGED_MSG']
260 ? _("Page now locked.")
261 : _("Page now unlocked.");
264 return $changed; // lock changed.
267 function savePage () {
268 $request = &$this->request;
270 if ($this->isUnchanged()) {
271 // Allow admin lock/unlock even if
272 // no text changes were made.
273 if ($this->updateLock()) {
274 $dbi = $request->getDbh();
277 // Save failed. No changes made.
278 $this->_redirectToBrowsePage();
279 // user will probably not see the rest of this...
280 require_once('lib/display.php');
281 // force browse of current version:
282 $request->setArg('action', false);
283 $request->setArg('version', false);
284 displayPage($request, 'nochanges');
288 if (!$this->user->isAdmin() and $this->isSpam()) {
289 $this->_isSpam = true;
292 // Save failed. No changes made.
293 $this->_redirectToBrowsePage();
294 // user will probably not see the rest of this...
295 require_once('lib/display.php');
296 // force browse of current version:
297 $request->setArg('version', false);
298 displayPage($request, 'nochanges');
303 $page = &$this->page;
305 // Include any meta-data from original page version which
306 // has not been explicitly updated.
307 // (Except don't propagate pgsrc_version --- moot for now,
308 // because at present it never gets into the db...)
309 $meta = $this->selected->getMetaData();
310 unset($meta['pgsrc_version']);
311 $meta = array_merge($meta, $this->meta);
314 $this->_content = $this->getContent();
315 $newrevision = $page->save($this->_content,
318 : $this->_currentVersion + 1,
321 if (!isa($newrevision, 'WikiDB_PageRevision')) {
322 // Save failed. (Concurrent updates).
326 // New contents successfully saved...
329 // Clean out archived versions of this page.
330 require_once('lib/ArchiveCleaner.php');
331 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
332 $cleaner->cleanPageRevisions($page);
334 /* generate notification emails done in WikiDB::save to catch
335 all direct calls (admin plugins) */
337 // look at the errorstack
338 $errors = $GLOBALS['ErrorManager']->_postponed_errors;
339 $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
340 $GLOBALS['ErrorManager']->_postponed_errors = $errors;
342 $dbi = $request->getDbh();
346 if (empty($warnings->_content) && ! $WikiTheme->getImageURL('signature')) {
347 // Do redirect to browse page if no signature has
348 // been defined. In this case, the user will most
349 // likely not see the rest of the HTML we generate
351 $request->setArg('action', false);
352 $this->_redirectToBrowsePage();
355 // Force browse of current page version.
356 $request->setArg('version', false);
357 // testme: does preview and more need action=edit?
358 $request->setArg('action', false);
360 $template = Template('savepage', $this->tokens);
361 $template->replace('CONTENT', $newrevision->getTransformedContent());
362 if (!empty($warnings->_content)) {
363 $template->replace('WARNINGS', $warnings);
364 unset($GLOBALS['ErrorManager']->_postponed_errors);
367 $pagelink = WikiLink($page);
369 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
373 function isConcurrentUpdate () {
374 assert($this->current->getVersion() >= $this->_currentVersion);
375 return $this->current->getVersion() != $this->_currentVersion;
378 function canEdit () {
379 return !$this->page->get('locked') || $this->user->isAdmin();
382 function isInitialEdit () {
383 return $this->_initialEdit;
386 function isUnchanged () {
387 $current = &$this->current;
389 if ($this->meta['markup'] != $current->get('markup'))
392 return $this->_content == $current->getPackedContent();
396 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
397 * Need to check dynamically some blacklist wikipage settings
398 * (plugin WikiAccessRestrictions) and some static blacklist.
400 * Always: More then 20 new external links
401 * ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
402 * ENABLE_SPAMBLOCKLIST: content domain blacklist
405 $current = &$this->current;
406 $request = &$this->request;
408 $oldtext = $current->getPackedContent();
409 $newtext =& $this->_content;
410 $numlinks = $this->numLinks($newtext);
411 $newlinks = $numlinks - $this->numLinks($oldtext);
412 // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
413 // better use a certain text : link ratio.
415 // 1. Not more then 20 new external links
416 if ($newlinks >= NUM_SPAM_LINKS)
418 // Allow strictly authenticated users?
419 // TODO: mail the admin?
420 $this->tokens['PAGE_LOCKED_MESSAGE'] =
421 HTML($this->getSpamMessage(),
422 HTML::p(HTML::strong(_("Too many external links."))));
425 // 2. external babycart (SpamAssassin) check
426 // This will probably prevent from discussing sex or viagra related topics. So beware.
427 if (ENABLE_SPAMASSASSIN) {
428 require_once("lib/spam_babycart.php");
429 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
430 $this->user->getId())) {
431 // TODO: mail the admin
432 if (is_array($babycart))
433 $this->tokens['PAGE_LOCKED_MESSAGE'] =
434 HTML($this->getSpamMessage(),
435 HTML::p(HTML::em(_("SpamAssassin reports: "),
436 join("\n", $babycart))));
440 // 3. extract (new) links and check surbl for blocked domains
441 if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
442 require_once("lib/SpamBlocklist.php");
443 require_once("lib/InlineParser.php");
444 $parsed = TransformLinks($newtext);
445 $oldparsed = TransformLinks($oldtext);
447 foreach ($oldparsed->_content as $link) {
448 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
449 $uri = $link->_getURL($this->page->getName());
454 foreach ($parsed->_content as $link) {
455 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
456 $uri = $link->_getURL($this->page->getName());
457 // only check new links, so admins may add blocked links.
458 if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
459 // TODO: mail the admin
460 $this->tokens['PAGE_LOCKED_MESSAGE'] =
461 HTML($this->getSpamMessage(),
462 HTML::p(HTML::strong(_("External links contain blocked domains:")),
463 HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
464 $uri." [".$res[2]."]", $res[0], $res[1])))));
477 /** Number of external links in the wikitext
479 function numLinks(&$text) {
480 return substr_count($text, "http://") + substr_count($text, "https://");
483 /** Header of the Anti Spam message
485 function getSpamMessage () {
487 HTML(HTML::h2(_("Spam Prevention")),
488 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
490 _("Sorry for the inconvenience.")),
494 function getPreview () {
495 require_once('lib/PageType.php');
496 $this->_content = $this->getContent();
497 return new TransformedText($this->page, $this->_content, $this->meta);
500 function getConvertedPreview () {
501 require_once('lib/PageType.php');
502 $this->_content = $this->getContent();
503 $this->meta['markup'] = 2.0;
504 $this->_content = ConvertOldMarkup($this->_content);
505 return new TransformedText($this->page, $this->_content, $this->meta);
508 function getDiff () {
509 require_once('lib/diff.php');
512 $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
513 if ($diff->isEmpty()) {
514 $html->pushContent(HTML::hr(),
515 HTML::p('[', _("Versions are identical"),
519 // New CSS formatted unified diffs (ugly in NS4).
520 $fmt = new HtmlUnifiedDiffFormatter;
521 // Use this for old table-formatted diffs.
522 //$fmt = new TableUnifiedDiffFormatter;
523 $html->pushContent($fmt->format($diff));
528 // possibly convert HTMLAREA content back to Wiki markup
529 function getContent () {
530 if (ENABLE_WYSIWYG) {
531 // don't store everything as html
532 if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
533 // Wikiwyg shortcut to avoid the InlineTransformer:
534 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
535 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
536 $this->_content = join("", $xml_output->_content);
538 $this->meta['pagetype'] = 'html';
540 return $this->_content;
542 return $this->_content;
546 function getLockedMessage () {
548 HTML(HTML::h2(_("Page Locked")),
549 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
550 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
551 HTML::p(_("Sorry for the inconvenience.")));
554 function isModerated() {
555 return $this->page->get('moderation');
557 function getModeratedMessage() {
559 HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
560 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")))),
561 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.",
562 WikiLink(_("UserPreferences")))));
564 function getConflictMessage ($unresolved = false) {
566 xgettext only knows about c/c++ line-continuation strings
567 it does not know about php's dot operator.
568 We want to translate this entire paragraph as one string, of course.
571 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
574 $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.",
575 "<<<<<<< ". _("Your version"),
576 ">>>>>>> ". _("Other version")));
578 $message = HTML::p(_("Please check it through before saving."));
582 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
583 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
585 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
586 HTML::li(_("Save your updated changes.")));
589 HTML(HTML::h2(_("Conflicting Edits!")),
590 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
591 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.")),
596 function getTextArea () {
597 $request = &$this->request;
599 $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
601 // WYSIWYG will need two pagetypes: raw wikitest and converted html
602 if (ENABLE_WYSIWYG) {
603 $this->_wikicontent = $this->_content;
604 $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
605 // $this->getPreview();
606 //$this->_htmlcontent = $this->_content->asXML();
609 $textarea = HTML::textarea(array('class'=> 'wikiedit',
610 'name' => 'edit[content]',
611 'id' => 'edit-content',
612 'rows' => $request->getPref('editHeight'),
613 'cols' => $request->getPref('editWidth'),
614 'readonly' => (bool) $readonly),
616 /** <textarea wrap="virtual"> is not valid XHTML but Netscape 4 requires it
617 * to wrap long lines.
620 $textarea->setAttr('wrap', 'virtual');
621 if (ENABLE_WYSIWYG) {
622 return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
623 $textarea->getAttr('name'));
628 function getFormElements () {
630 $request = &$this->request;
631 $page = &$this->page;
633 $h = array('action' => 'edit',
634 'pagename' => $page->getName(),
635 'version' => $this->version,
636 'edit[pagetype]' => $this->meta['pagetype'],
637 'edit[current_version]' => $this->_currentVersion);
639 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
640 $el['EDIT_TEXTAREA'] = $this->getTextArea();
641 if ( ENABLE_CAPTCHA ) {
642 $el = array_merge($el, $this->Captcha->getFormElements());
645 = HTML::input(array('type' => 'text',
646 'class' => 'wikitext',
647 'id' => 'edit-summary',
648 'name' => 'edit[summary]',
651 'value' => $this->meta['summary']));
653 = HTML::input(array('type' => 'checkbox',
654 'name' => 'edit[minor_edit]',
655 'id' => 'edit-minor_edit',
656 'checked' => (bool) $this->meta['is_minor_edit']));
658 = HTML::input(array('type' => 'checkbox',
659 'name' => 'edit[markup]',
661 'checked' => $this->meta['markup'] < 2.0,
662 'id' => 'useOldMarkup',
663 'onclick' => 'showOldMarkupRules(this.checked)'));
664 $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0)
665 ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
667 = HTML::input(array('type' => 'checkbox',
668 'name' => 'edit[locked]',
669 'id' => 'edit-locked',
670 'disabled' => (bool) !$this->user->isadmin(),
671 'checked' => (bool) $this->locked));
673 if (ENABLE_PAGE_PUBLIC) {
675 = HTML::input(array('type' => 'checkbox',
676 'name' => 'edit[public]',
677 'id' => 'edit-public',
678 'disabled' => (bool) !$this->user->isAdmin(),
679 'checked' => (bool) $this->page->get('public')));
681 if (ENABLE_WYSIWYG) {
682 if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
683 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
687 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
689 array('accesskey'=> 'p',
690 'title' => 'Preview the current content [alt-p]'));
692 //if (!$this->isConcurrentUpdate() && $this->canEdit())
693 $el['SAVE_B'] = Button('submit:edit[save]',
694 _("Save"), 'wikiaction',
695 array('accesskey'=> 's',
696 'title' => 'Save the current content as wikipage [alt-s]'));
697 $el['CHANGES_B'] = Button('submit:edit[diff]',
698 _("Changes"), 'wikiaction',
699 array('accesskey'=> 'c',
700 'title' => 'Preview the current changes as diff [alt-c]'));
701 $el['UPLOAD_B'] = Button('submit:edit[upload]',
702 _("Upload"), 'wikiaction',
703 array('title' => 'Select a local file and press Upload to attach into this page'));
704 $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
705 _("Spell Check"), 'wikiaction',
706 array('title' => 'Check the spelling'));
707 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
710 = HTML::input(array('type' => 'text',
713 'class' => "numeric",
714 'name' => 'pref[editWidth]',
715 'id' => 'pref-editWidth',
716 'value' => $request->getPref('editWidth'),
717 'onchange' => 'this.form.submit();'));
719 = HTML::input(array('type' => 'text',
722 'class' => "numeric",
723 'name' => 'pref[editHeight]',
724 'id' => 'pref-editHeight',
725 'value' => $request->getPref('editHeight'),
726 'onchange' => 'this.form.submit();'));
727 $el['SEP'] = $WikiTheme->getButtonSeparator();
728 $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
729 HTML::em($this->user->getId()));
734 function _redirectToBrowsePage() {
735 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
738 function _restoreState () {
739 $request = &$this->request;
741 $posted = $request->getArg('edit');
742 $request->setArg('edit', false);
745 || !$request->isPost()
746 || !in_array($request->getArg('action'),array('edit','loadfile')))
749 if (!isset($posted['content']) || !is_string($posted['content']))
751 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
752 rtrim($posted['content']));
753 $this->_content = $this->getContent();
755 $this->_currentVersion = (int) $posted['current_version'];
757 if ($this->_currentVersion < 0)
759 if ($this->_currentVersion > $this->current->getVersion())
760 return false; // FIXME: some kind of warning?
762 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
763 $meta['markup'] = $is_old_markup ? false : 2.0;
764 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
765 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
766 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
767 if ( ENABLE_CAPTCHA )
768 $meta['captcha_input'] = !empty($posted['captcha_input']) ?
769 $posted['captcha_input'] : '';
771 $this->meta = array_merge($this->meta, $meta);
772 $this->locked = !empty($posted['locked']);
773 if (ENABLE_PAGE_PUBLIC)
774 $this->public = !empty($posted['public']);
776 foreach (array('preview','save','edit_convert',
777 'keep_old','overwrite','diff','upload') as $o)
779 if (!empty($posted[$o]))
780 $this->editaction = $o;
782 if (empty($this->editaction))
783 $this->editaction = 'edit';
788 function _initializeState () {
789 $request = &$this->request;
790 $current = &$this->current;
791 $selected = &$this->selected;
792 $user = &$this->user;
795 NoSuchRevision($request, $this->page, $this->version); // noreturn
797 $this->_currentVersion = $current->getVersion();
798 $this->_content = $selected->getPackedContent();
800 $this->locked = $this->page->get('locked');
802 // If author same as previous author, default minor_edit to on.
803 $age = $this->meta['mtime'] - $current->get('mtime');
804 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
805 && $current->get('author') == $user->getId()
808 // Default for new pages is new-style markup.
809 if ($selected->hasDefaultContents())
810 $is_new_markup = true;
812 $is_new_markup = $selected->get('markup') >= 2.0;
814 $this->meta['markup'] = $is_new_markup ? 2.0: false;
815 $this->meta['pagetype'] = $selected->get('pagetype');
816 if ($this->meta['pagetype'] == 'wikiblog')
817 $this->meta['summary'] = $selected->get('summary'); // keep blog title
819 $this->meta['summary'] = '';
820 $this->editaction = 'edit';
824 class LoadFileConflictPageEditor
827 function editPage ($saveFailed = true) {
828 $tokens = &$this->tokens;
830 if (!$this->canEdit()) {
831 if ($this->isInitialEdit()) {
832 return $this->viewSource();
834 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
836 elseif ($this->editaction == 'save') {
837 if ($this->savePage()) {
838 return true; // Page saved.
843 if ($saveFailed || $this->isConcurrentUpdate())
845 // Get the text of the original page, and the two conflicting edits
846 // The diff class takes arrays as input. So retrieve content as
847 // an array, or convert it as necesary.
848 $orig = $this->page->getRevision($this->_currentVersion);
849 $this_content = explode("\n", $this->_content);
850 $other_content = $this->current->getContent();
851 require_once("lib/diff.php");
852 $diff2 = new Diff($other_content, $this_content);
853 $context_lines = max(4, count($other_content) + 1,
854 count($this_content) + 1);
855 $fmt = new BlockDiffFormatter($context_lines);
857 $this->_content = $fmt->format($diff2);
858 // FIXME: integrate this into class BlockDiffFormatter
859 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
861 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
864 $this->_currentVersion = $this->current->getVersion();
865 $this->version = $this->_currentVersion;
866 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
869 if ($this->editaction == 'edit_convert')
870 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
871 if ($this->editaction == 'preview')
872 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
874 // FIXME: NOT_CURRENT_MESSAGE?
875 $tokens = array_merge($tokens, $this->getFormElements());
876 // we need all GET params for loadfile overwrite
877 if ($this->request->getArg('action') == 'loadfile') {
879 $this->tokens['HIDDEN_INPUTS'] =
881 (array('source' => $this->request->getArg('source'),
883 $this->tokens['HIDDEN_INPUTS']);
884 // add two conflict resolution buttons before preview and save.
885 $tokens['PREVIEW_B'] = HTML(
886 Button('submit:edit[keep_old]',
887 _("Keep old"), 'wikiaction'),
889 Button('submit:edit[overwrite]',
890 _("Overwrite with new"), 'wikiaction'),
892 $tokens['PREVIEW_B']);
894 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
895 include_once("lib/EditToolbar.php");
896 $toolbar = new EditToolbar();
897 $tokens = array_merge($tokens, $toolbar->getTokens());
900 return $this->output('editpage', _("Merge and Edit: %s"));
903 function output ($template, $title_fs) {
904 $selected = &$this->selected;
905 $current = &$this->current;
907 if ($selected && $selected->getVersion() != $current->getVersion()) {
909 $pagelink = WikiLink($selected);
913 $pagelink = WikiLink($this->page);
916 $title = new FormattedText ($title_fs, $pagelink);
917 $this->tokens['HEADER'] = $title;
918 //hack! there's no TITLE in editpage, but in the previous top template
919 if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
920 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
922 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
923 $this->tokens['PAGE_LOCKED_MESSAGE']);
924 $template = Template($template, $this->tokens);
926 //GeneratePage($template, $title, $rev);
931 function getConflictMessage () {
932 $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.",
935 HTML::p(_("Please check it through before saving."))));
944 // c-hanging-comment-ender-p: nil
945 // indent-tabs-mode: nil