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 (!ENABLE_PAGE_PUBLIC && !ENABLE_EXTERNAL_PAGES) {
239 if ((bool)$this->page->get('locked') == (bool)$this->locked)
240 return false; // Not changed.
243 if (!$this->user->isAdmin()) {
244 // FIXME: some sort of message
245 return false; // not allowed.
247 if ((bool)$this->page->get('locked') != (bool)$this->locked) {
248 $this->page->set('locked', (bool)$this->locked);
249 $this->tokens['LOCK_CHANGED_MSG']
251 ? _("Page now locked.")
252 : _("Page now unlocked.") . " ");
255 if (ENABLE_PAGE_PUBLIC and (bool)$this->page->get('public') != (bool)$this->public) {
256 $this->page->set('public', (bool)$this->public);
257 $this->tokens['LOCK_CHANGED_MSG']
259 ? _("Page now public.")
260 : _("Page now not-public."));
264 if (ENABLE_EXTERNAL_PAGES) {
265 if ((bool)$this->page->get('external') != (bool)$this->external) {
266 $this->page->set('external', (bool)$this->external);
267 $this->tokens['LOCK_CHANGED_MSG']
269 ? _("Page now external.")
270 : _("Page now not-external.")) . " ";
274 return $changed; // lock changed.
277 function savePage () {
278 $request = &$this->request;
280 if ($this->isUnchanged()) {
281 // Allow admin lock/unlock even if
282 // no text changes were made.
283 if ($this->updateLock()) {
284 $dbi = $request->getDbh();
287 // Save failed. No changes made.
288 $this->_redirectToBrowsePage();
289 // user will probably not see the rest of this...
290 require_once('lib/display.php');
291 // force browse of current version:
292 $request->setArg('action', false);
293 $request->setArg('version', false);
294 displayPage($request, 'nochanges');
298 if (!$this->user->isAdmin() and $this->isSpam()) {
299 $this->_isSpam = true;
302 // Save failed. No changes made.
303 $this->_redirectToBrowsePage();
304 // user will probably not see the rest of this...
305 require_once('lib/display.php');
306 // force browse of current version:
307 $request->setArg('version', false);
308 displayPage($request, 'nochanges');
313 $page = &$this->page;
315 // Include any meta-data from original page version which
316 // has not been explicitly updated.
317 // (Except don't propagate pgsrc_version --- moot for now,
318 // because at present it never gets into the db...)
319 $meta = $this->selected->getMetaData();
320 unset($meta['pgsrc_version']);
321 $meta = array_merge($meta, $this->meta);
324 $this->_content = $this->getContent();
325 $newrevision = $page->save($this->_content,
328 : $this->_currentVersion + 1,
331 if (!isa($newrevision, 'WikiDB_PageRevision')) {
332 // Save failed. (Concurrent updates).
336 // New contents successfully saved...
339 // Clean out archived versions of this page.
340 require_once('lib/ArchiveCleaner.php');
341 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
342 $cleaner->cleanPageRevisions($page);
344 /* generate notification emails done in WikiDB::save to catch
345 all direct calls (admin plugins) */
347 // look at the errorstack
348 $errors = $GLOBALS['ErrorManager']->_postponed_errors;
349 $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
350 $GLOBALS['ErrorManager']->_postponed_errors = $errors;
352 $dbi = $request->getDbh();
356 if (empty($warnings->_content) && ! $WikiTheme->getImageURL('signature')) {
357 // Do redirect to browse page if no signature has
358 // been defined. In this case, the user will most
359 // likely not see the rest of the HTML we generate
361 $request->setArg('action', false);
362 $this->_redirectToBrowsePage();
365 // Force browse of current page version.
366 $request->setArg('version', false);
367 // testme: does preview and more need action=edit?
368 $request->setArg('action', false);
370 $template = Template('savepage', $this->tokens);
371 $template->replace('CONTENT', $newrevision->getTransformedContent());
372 if (!empty($warnings->_content)) {
373 $template->replace('WARNINGS', $warnings);
374 unset($GLOBALS['ErrorManager']->_postponed_errors);
377 $pagelink = WikiLink($page);
379 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
383 function isConcurrentUpdate () {
384 assert($this->current->getVersion() >= $this->_currentVersion);
385 return $this->current->getVersion() != $this->_currentVersion;
388 function canEdit () {
389 return !$this->page->get('locked') || $this->user->isAdmin();
392 function isInitialEdit () {
393 return $this->_initialEdit;
396 function isUnchanged () {
397 $current = &$this->current;
399 if ($this->meta['markup'] != $current->get('markup'))
402 return $this->_content == $current->getPackedContent();
406 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
407 * Need to check dynamically some blacklist wikipage settings
408 * (plugin WikiAccessRestrictions) and some static blacklist.
410 * More than NUM_SPAM_LINKS (default: 20) new external links.
411 * Disabled if NUM_SPAM_LINKS is 0
412 * ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
413 * ENABLE_SPAMBLOCKLIST: content domain blacklist
416 $current = &$this->current;
417 $request = &$this->request;
419 $oldtext = $current->getPackedContent();
420 $newtext =& $this->_content;
421 $numlinks = $this->numLinks($newtext);
422 $newlinks = $numlinks - $this->numLinks($oldtext);
423 // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
424 // better use a certain text : link ratio.
426 // 1. Not more than NUM_SPAM_LINKS (default: 20) new external links
427 if ((NUM_SPAM_LINKS > 0) and ($newlinks >= NUM_SPAM_LINKS))
429 // Allow strictly authenticated users?
430 // TODO: mail the admin?
431 $this->tokens['PAGE_LOCKED_MESSAGE'] =
432 HTML($this->getSpamMessage(),
433 HTML::p(HTML::strong(_("Too many external links."))));
436 // 2. external babycart (SpamAssassin) check
437 // This will probably prevent from discussing sex or viagra related topics. So beware.
438 if (ENABLE_SPAMASSASSIN) {
439 require_once("lib/spam_babycart.php");
440 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
441 $this->user->getId())) {
442 // TODO: mail the admin
443 if (is_array($babycart))
444 $this->tokens['PAGE_LOCKED_MESSAGE'] =
445 HTML($this->getSpamMessage(),
446 HTML::p(HTML::em(_("SpamAssassin reports: "),
447 join("\n", $babycart))));
451 // 3. extract (new) links and check surbl for blocked domains
452 if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
453 require_once("lib/SpamBlocklist.php");
454 require_once("lib/InlineParser.php");
455 $parsed = TransformLinks($newtext);
456 $oldparsed = TransformLinks($oldtext);
458 foreach ($oldparsed->_content as $link) {
459 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
460 $uri = $link->_getURL($this->page->getName());
465 foreach ($parsed->_content as $link) {
466 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
467 $uri = $link->_getURL($this->page->getName());
468 // only check new links, so admins may add blocked links.
469 if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
470 // TODO: mail the admin
471 $this->tokens['PAGE_LOCKED_MESSAGE'] =
472 HTML($this->getSpamMessage(),
473 HTML::p(HTML::strong(_("External links contain blocked domains:")),
474 HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
475 $uri." [".$res[2]."]", $res[0], $res[1])))));
488 /** Number of external links in the wikitext
490 function numLinks(&$text) {
491 return substr_count($text, "http://") + substr_count($text, "https://");
494 /** Header of the Anti Spam message
496 function getSpamMessage () {
498 HTML(HTML::h2(_("Spam Prevention")),
499 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
501 _("Sorry for the inconvenience.")),
505 function getPreview () {
506 require_once('lib/PageType.php');
507 $this->_content = $this->getContent();
508 return new TransformedText($this->page, $this->_content, $this->meta);
511 function getConvertedPreview () {
512 require_once('lib/PageType.php');
513 $this->_content = $this->getContent();
514 $this->meta['markup'] = 2.0;
515 $this->_content = ConvertOldMarkup($this->_content);
516 return new TransformedText($this->page, $this->_content, $this->meta);
519 function getDiff () {
520 require_once('lib/diff.php');
523 $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
524 if ($diff->isEmpty()) {
525 $html->pushContent(HTML::hr(),
526 HTML::p('[', _("Versions are identical"),
530 // New CSS formatted unified diffs (ugly in NS4).
531 $fmt = new HtmlUnifiedDiffFormatter;
532 // Use this for old table-formatted diffs.
533 //$fmt = new TableUnifiedDiffFormatter;
534 $html->pushContent($fmt->format($diff));
539 // possibly convert HTMLAREA content back to Wiki markup
540 function getContent () {
541 if (ENABLE_WYSIWYG) {
542 // don't store everything as html
543 if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
544 // Wikiwyg shortcut to avoid the InlineTransformer:
545 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
546 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
547 $this->_content = join("", $xml_output->_content);
549 $this->meta['pagetype'] = 'html';
551 return $this->_content;
553 return $this->_content;
557 function getLockedMessage () {
559 HTML(HTML::h2(_("Page Locked")),
560 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
561 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
562 HTML::p(_("Sorry for the inconvenience.")));
565 function isModerated() {
566 return $this->page->get('moderation');
568 function getModeratedMessage() {
570 HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
571 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")))),
572 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.",
573 WikiLink(_("UserPreferences")))));
575 function getConflictMessage ($unresolved = false) {
577 xgettext only knows about c/c++ line-continuation strings
578 it does not know about php's dot operator.
579 We want to translate this entire paragraph as one string, of course.
582 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
585 $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.",
586 "<<<<<<< ". _("Your version"),
587 ">>>>>>> ". _("Other version")));
589 $message = HTML::p(_("Please check it through before saving."));
593 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
594 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
596 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
597 HTML::li(_("Save your updated changes.")));
600 HTML(HTML::h2(_("Conflicting Edits!")),
601 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
602 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.")),
607 function getTextArea () {
608 $request = &$this->request;
610 $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
612 // WYSIWYG will need two pagetypes: raw wikitest and converted html
613 if (ENABLE_WYSIWYG) {
614 $this->_wikicontent = $this->_content;
615 $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
616 // $this->getPreview();
617 //$this->_htmlcontent = $this->_content->asXML();
620 $textarea = HTML::textarea(array('class'=> 'wikiedit',
621 'name' => 'edit[content]',
622 'id' => 'edit-content',
623 'rows' => $request->getPref('editHeight'),
624 'cols' => $request->getPref('editWidth'),
625 'readonly' => (bool) $readonly),
627 /** <textarea wrap="virtual"> is not valid XHTML but Netscape 4 requires it
628 * to wrap long lines.
631 $textarea->setAttr('wrap', 'virtual');
632 if (ENABLE_WYSIWYG) {
633 return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
634 $textarea->getAttr('name'));
639 function getFormElements () {
641 $request = &$this->request;
642 $page = &$this->page;
644 $h = array('action' => 'edit',
645 'pagename' => $page->getName(),
646 'version' => $this->version,
647 'edit[pagetype]' => $this->meta['pagetype'],
648 'edit[current_version]' => $this->_currentVersion);
650 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
651 $el['EDIT_TEXTAREA'] = $this->getTextArea();
652 if ( ENABLE_CAPTCHA ) {
653 $el = array_merge($el, $this->Captcha->getFormElements());
656 = HTML::input(array('type' => 'text',
657 'class' => 'wikitext',
658 'id' => 'edit-summary',
659 'name' => 'edit[summary]',
662 'value' => $this->meta['summary']));
664 = HTML::input(array('type' => 'checkbox',
665 'name' => 'edit[minor_edit]',
666 'id' => 'edit-minor_edit',
667 'checked' => (bool) $this->meta['is_minor_edit']));
669 = HTML::input(array('type' => 'checkbox',
670 'name' => 'edit[markup]',
672 'checked' => $this->meta['markup'] < 2.0,
673 'id' => 'useOldMarkup',
674 'onclick' => 'showOldMarkupRules(this.checked)'));
675 $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0)
676 ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
678 = HTML::input(array('type' => 'checkbox',
679 'name' => 'edit[locked]',
680 'id' => 'edit-locked',
681 'disabled' => (bool) !$this->user->isAdmin(),
682 'checked' => (bool) $this->locked));
683 if (ENABLE_PAGE_PUBLIC) {
685 = HTML::input(array('type' => 'checkbox',
686 'name' => 'edit[public]',
687 'id' => 'edit-public',
688 'disabled' => (bool) !$this->user->isAdmin(),
689 'checked' => (bool) $this->page->get('public')));
691 if (ENABLE_EXTERNAL_PAGES) {
693 = HTML::input(array('type' => 'checkbox',
694 'name' => 'edit[external]',
695 'id' => 'edit-external',
696 'disabled' => (bool) !$this->user->isAdmin(),
697 'checked' => (bool) $this->page->get('external')));
699 if (ENABLE_WYSIWYG) {
700 if (($this->version == 0) and ($request->getArg('mode') != 'wysiwyg')) {
701 $el['WYSIWYG_B'] = Button(array("action" => "edit", "mode" => "wysiwyg"), "Wysiwyg Editor");
705 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
707 array('accesskey'=> 'p',
708 'title' => 'Preview the current content [alt-p]'));
710 //if (!$this->isConcurrentUpdate() && $this->canEdit())
711 $el['SAVE_B'] = Button('submit:edit[save]',
712 _("Save"), 'wikiaction',
713 array('accesskey'=> 's',
714 'title' => 'Save the current content as wikipage [alt-s]'));
715 $el['CHANGES_B'] = Button('submit:edit[diff]',
716 _("Changes"), 'wikiaction',
717 array('accesskey'=> 'c',
718 'title' => 'Preview the current changes as diff [alt-c]'));
719 $el['UPLOAD_B'] = Button('submit:edit[upload]',
720 _("Upload"), 'wikiaction',
721 array('title' => 'Select a local file and press Upload to attach into this page'));
722 $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
723 _("Spell Check"), 'wikiaction',
724 array('title' => 'Check the spelling'));
725 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
728 = HTML::input(array('type' => 'text',
731 'class' => "numeric",
732 'name' => 'pref[editWidth]',
733 'id' => 'pref-editWidth',
734 'value' => $request->getPref('editWidth'),
735 'onchange' => 'this.form.submit();'));
737 = HTML::input(array('type' => 'text',
740 'class' => "numeric",
741 'name' => 'pref[editHeight]',
742 'id' => 'pref-editHeight',
743 'value' => $request->getPref('editHeight'),
744 'onchange' => 'this.form.submit();'));
745 $el['SEP'] = $WikiTheme->getButtonSeparator();
746 $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
747 HTML::em($this->user->getId()));
752 function _redirectToBrowsePage() {
753 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
756 function _restoreState () {
757 $request = &$this->request;
759 $posted = $request->getArg('edit');
760 $request->setArg('edit', false);
763 || !$request->isPost()
764 || !in_array($request->getArg('action'),array('edit','loadfile')))
767 if (!isset($posted['content']) || !is_string($posted['content']))
769 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
770 rtrim($posted['content']));
771 $this->_content = $this->getContent();
773 $this->_currentVersion = (int) $posted['current_version'];
775 if ($this->_currentVersion < 0)
777 if ($this->_currentVersion > $this->current->getVersion())
778 return false; // FIXME: some kind of warning?
780 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
781 $meta['markup'] = $is_old_markup ? false : 2.0;
782 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
783 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
784 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
785 if ( ENABLE_CAPTCHA )
786 $meta['captcha_input'] = !empty($posted['captcha_input']) ?
787 $posted['captcha_input'] : '';
789 $this->meta = array_merge($this->meta, $meta);
790 $this->locked = !empty($posted['locked']);
791 if (ENABLE_PAGE_PUBLIC)
792 $this->public = !empty($posted['public']);
793 if (ENABLE_EXTERNAL_PAGES)
794 $this->external = !empty($posted['external']);
796 foreach (array('preview','save','edit_convert',
797 'keep_old','overwrite','diff','upload') as $o)
799 if (!empty($posted[$o]))
800 $this->editaction = $o;
802 if (empty($this->editaction))
803 $this->editaction = 'edit';
808 function _initializeState () {
809 $request = &$this->request;
810 $current = &$this->current;
811 $selected = &$this->selected;
812 $user = &$this->user;
815 NoSuchRevision($request, $this->page, $this->version); // noreturn
817 $this->_currentVersion = $current->getVersion();
818 $this->_content = $selected->getPackedContent();
820 $this->locked = $this->page->get('locked');
822 // If author same as previous author, default minor_edit to on.
823 $age = $this->meta['mtime'] - $current->get('mtime');
824 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
825 && $current->get('author') == $user->getId()
828 // Default for new pages is new-style markup.
829 if ($selected->hasDefaultContents())
830 $is_new_markup = true;
832 $is_new_markup = $selected->get('markup') >= 2.0;
834 $this->meta['markup'] = $is_new_markup ? 2.0: false;
835 $this->meta['pagetype'] = $selected->get('pagetype');
836 if ($this->meta['pagetype'] == 'wikiblog')
837 $this->meta['summary'] = $selected->get('summary'); // keep blog title
839 $this->meta['summary'] = '';
840 $this->editaction = 'edit';
844 class LoadFileConflictPageEditor
847 function editPage ($saveFailed = true) {
848 $tokens = &$this->tokens;
850 if (!$this->canEdit()) {
851 if ($this->isInitialEdit()) {
852 return $this->viewSource();
854 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
856 elseif ($this->editaction == 'save') {
857 if ($this->savePage()) {
858 return true; // Page saved.
863 if ($saveFailed || $this->isConcurrentUpdate())
865 // Get the text of the original page, and the two conflicting edits
866 // The diff class takes arrays as input. So retrieve content as
867 // an array, or convert it as necesary.
868 $orig = $this->page->getRevision($this->_currentVersion);
869 $this_content = explode("\n", $this->_content);
870 $other_content = $this->current->getContent();
871 require_once("lib/diff.php");
872 $diff2 = new Diff($other_content, $this_content);
873 $context_lines = max(4, count($other_content) + 1,
874 count($this_content) + 1);
875 $fmt = new BlockDiffFormatter($context_lines);
877 $this->_content = $fmt->format($diff2);
878 // FIXME: integrate this into class BlockDiffFormatter
879 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
881 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
884 $this->_currentVersion = $this->current->getVersion();
885 $this->version = $this->_currentVersion;
886 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
889 if ($this->editaction == 'edit_convert')
890 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
891 if ($this->editaction == 'preview')
892 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
894 // FIXME: NOT_CURRENT_MESSAGE?
895 $tokens = array_merge($tokens, $this->getFormElements());
896 // we need all GET params for loadfile overwrite
897 if ($this->request->getArg('action') == 'loadfile') {
899 $this->tokens['HIDDEN_INPUTS'] =
901 (array('source' => $this->request->getArg('source'),
903 $this->tokens['HIDDEN_INPUTS']);
904 // add two conflict resolution buttons before preview and save.
905 $tokens['PREVIEW_B'] = HTML(
906 Button('submit:edit[keep_old]',
907 _("Keep old"), 'wikiaction'),
909 Button('submit:edit[overwrite]',
910 _("Overwrite with new"), 'wikiaction'),
912 $tokens['PREVIEW_B']);
914 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
915 include_once("lib/EditToolbar.php");
916 $toolbar = new EditToolbar();
917 $tokens = array_merge($tokens, $toolbar->getTokens());
920 return $this->output('editpage', _("Merge and Edit: %s"));
923 function output ($template, $title_fs) {
924 $selected = &$this->selected;
925 $current = &$this->current;
927 if ($selected && $selected->getVersion() != $current->getVersion()) {
929 $pagelink = WikiLink($selected);
933 $pagelink = WikiLink($this->page);
936 $title = new FormattedText ($title_fs, $pagelink);
937 $this->tokens['HEADER'] = $title;
938 //hack! there's no TITLE in editpage, but in the previous top template
939 if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
940 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
942 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
943 $this->tokens['PAGE_LOCKED_MESSAGE']);
944 $template = Template($template, $this->tokens);
946 //GeneratePage($template, $title, $rev);
951 function getConflictMessage () {
952 $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.",
955 HTML::p(_("Please check it through before saving."))));
964 // c-hanging-comment-ender-p: nil
965 // indent-tabs-mode: nil