2 rcs_id('$Id: editpage.php,v 1.113 2007-07-14 12:04:12 rurban Exp $');
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() {
236 if ((bool)$this->page->get('locked') == (bool)$this->locked)
237 return false; // Not changed.
239 if (!$this->user->isAdmin()) {
240 // FIXME: some sort of message
241 return false; // not allowed.
244 $this->page->set('locked', (bool)$this->locked);
245 $this->tokens['LOCK_CHANGED_MSG']
246 = $this->locked ? _("Page now locked.") : _("Page now unlocked.");
248 return true; // lock changed.
251 function savePage () {
252 $request = &$this->request;
254 if ($this->isUnchanged()) {
255 // Allow admin lock/unlock even if
256 // no text changes were made.
257 if ($this->updateLock()) {
258 $dbi = $request->getDbh();
261 // Save failed. No changes made.
262 $this->_redirectToBrowsePage();
263 // user will probably not see the rest of this...
264 require_once('lib/display.php');
265 // force browse of current version:
266 $request->setArg('action', false);
267 $request->setArg('version', false);
268 displayPage($request, 'nochanges');
272 if (!$this->user->isAdmin() and $this->isSpam()) {
273 $this->_isSpam = true;
276 // Save failed. No changes made.
277 $this->_redirectToBrowsePage();
278 // user will probably not see the rest of this...
279 require_once('lib/display.php');
280 // force browse of current version:
281 $request->setArg('version', false);
282 displayPage($request, 'nochanges');
287 $page = &$this->page;
289 // Include any meta-data from original page version which
290 // has not been explicitly updated.
291 // (Except don't propagate pgsrc_version --- moot for now,
292 // because at present it never gets into the db...)
293 $meta = $this->selected->getMetaData();
294 unset($meta['pgsrc_version']);
295 $meta = array_merge($meta, $this->meta);
298 $this->_content = $this->getContent();
299 $newrevision = $page->save($this->_content,
302 : $this->_currentVersion + 1,
305 if (!isa($newrevision, 'WikiDB_PageRevision')) {
306 // Save failed. (Concurrent updates).
310 // New contents successfully saved...
313 // Clean out archived versions of this page.
314 require_once('lib/ArchiveCleaner.php');
315 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
316 $cleaner->cleanPageRevisions($page);
318 /* generate notification emails done in WikiDB::save to catch
319 all direct calls (admin plugins) */
321 // look at the errorstack
322 $errors = $GLOBALS['ErrorManager']->_postponed_errors;
323 $warnings = $GLOBALS['ErrorManager']->getPostponedErrorsAsHTML();
324 $GLOBALS['ErrorManager']->_postponed_errors = $errors;
326 $dbi = $request->getDbh();
330 if (empty($warnings->_content) && ! $WikiTheme->getImageURL('signature')) {
331 // Do redirect to browse page if no signature has
332 // been defined. In this case, the user will most
333 // likely not see the rest of the HTML we generate
335 $request->setArg('action', false);
336 $this->_redirectToBrowsePage();
339 // Force browse of current page version.
340 $request->setArg('version', false);
341 // testme: does preview and more need action=edit?
342 $request->setArg('action', false);
344 $template = Template('savepage', $this->tokens);
345 $template->replace('CONTENT', $newrevision->getTransformedContent());
346 if (!empty($warnings->_content)) {
347 $template->replace('WARNINGS', $warnings);
348 unset($GLOBALS['ErrorManager']->_postponed_errors);
351 $pagelink = WikiLink($page);
353 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
357 function isConcurrentUpdate () {
358 assert($this->current->getVersion() >= $this->_currentVersion);
359 return $this->current->getVersion() != $this->_currentVersion;
362 function canEdit () {
363 return !$this->page->get('locked') || $this->user->isAdmin();
366 function isInitialEdit () {
367 return $this->_initialEdit;
370 function isUnchanged () {
371 $current = &$this->current;
373 if ($this->meta['markup'] != $current->get('markup'))
376 return $this->_content == $current->getPackedContent();
380 * Handle AntiSpam here. How? http://wikiblacklist.blogspot.com/
381 * Need to check dynamically some blacklist wikipage settings
382 * (plugin WikiAccessRestrictions) and some static blacklist.
384 * Always: More then 20 new external links
385 * ENABLE_SPAMASSASSIN: content patterns by babycart (only php >= 4.3 for now)
386 * ENABLE_SPAMBLOCKLIST: content domain blacklist
389 $current = &$this->current;
390 $request = &$this->request;
392 $oldtext = $current->getPackedContent();
393 $newtext =& $this->_content;
394 $numlinks = $this->numLinks($newtext);
395 $newlinks = $numlinks - $this->numLinks($oldtext);
396 // FIXME: in longer texts the NUM_SPAM_LINKS number should be increased.
397 // better use a certain text : link ratio.
399 // 1. Not more then 20 new external links
400 if ($newlinks >= NUM_SPAM_LINKS)
402 // Allow strictly authenticated users?
403 // TODO: mail the admin?
404 $this->tokens['PAGE_LOCKED_MESSAGE'] =
405 HTML($this->getSpamMessage(),
406 HTML::p(HTML::strong(_("Too many external links."))));
409 // 2. external babycart (SpamAssassin) check
410 // This will probably prevent from discussing sex or viagra related topics. So beware.
411 if (ENABLE_SPAMASSASSIN) {
412 require_once("lib/spam_babycart.php");
413 if ($babycart = check_babycart($newtext, $request->get("REMOTE_ADDR"),
414 $this->user->getId())) {
415 // TODO: mail the admin
416 if (is_array($babycart))
417 $this->tokens['PAGE_LOCKED_MESSAGE'] =
418 HTML($this->getSpamMessage(),
419 HTML::p(HTML::em(_("SpamAssassin reports: "),
420 join("\n", $babycart))));
424 // 3. extract (new) links and check surbl for blocked domains
425 if (ENABLE_SPAMBLOCKLIST and ($newlinks > 5)) {
426 require_once("lib/SpamBlocklist.php");
427 require_once("lib/InlineParser.php");
428 $parsed = TransformLinks($newtext);
429 $oldparsed = TransformLinks($oldtext);
431 foreach ($oldparsed->_content as $link) {
432 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
433 $uri = $link->_getURL($this->page->getName());
438 foreach ($parsed->_content as $link) {
439 if (isa($link, 'Cached_ExternalLink') and !isa($link, 'Cached_InterwikiLink')) {
440 $uri = $link->_getURL($this->page->getName());
441 // only check new links, so admins may add blocked links.
442 if (!array_key_exists($uri, $oldlinks) and ($res = IsBlackListed($uri))) {
443 // TODO: mail the admin
444 $this->tokens['PAGE_LOCKED_MESSAGE'] =
445 HTML($this->getSpamMessage(),
446 HTML::p(HTML::strong(_("External links contain blocked domains:")),
447 HTML::ul(HTML::li(sprintf(_("%s is listed at %s with %s"),
448 $uri." [".$res[2]."]", $res[0], $res[1])))));
461 /** Number of external links in the wikitext
463 function numLinks(&$text) {
464 return substr_count($text, "http://") + substr_count($text, "https://");
467 /** Header of the Anti Spam message
469 function getSpamMessage () {
471 HTML(HTML::h2(_("Spam Prevention")),
472 HTML::p(_("This page edit seems to contain spam and was therefore not saved."),
474 _("Sorry for the inconvenience.")),
478 function getPreview () {
479 require_once('lib/PageType.php');
480 $this->_content = $this->getContent();
481 return new TransformedText($this->page, $this->_content, $this->meta);
484 function getConvertedPreview () {
485 require_once('lib/PageType.php');
486 $this->_content = $this->getContent();
487 $this->meta['markup'] = 2.0;
488 $this->_content = ConvertOldMarkup($this->_content);
489 return new TransformedText($this->page, $this->_content, $this->meta);
492 function getDiff () {
493 require_once('lib/diff.php');
496 $diff = new Diff($this->current->getContent(), explode("\n", $this->getContent()));
497 if ($diff->isEmpty()) {
498 $html->pushContent(HTML::hr(),
499 HTML::p('[', _("Versions are identical"),
503 // New CSS formatted unified diffs (ugly in NS4).
504 $fmt = new HtmlUnifiedDiffFormatter;
505 // Use this for old table-formatted diffs.
506 //$fmt = new TableUnifiedDiffFormatter;
507 $html->pushContent($fmt->format($diff));
512 // possibly convert HTMLAREA content back to Wiki markup
513 function getContent () {
514 if (ENABLE_WYSIWYG) {
515 // don't store everything as html
516 if (!WYSIWYG_DEFAULT_PAGETYPE_HTML) {
517 // Wikiwyg shortcut to avoid the InlineTransformer:
518 if (WYSIWYG_BACKEND == "Wikiwyg") return $this->_content;
519 $xml_output = $this->WysiwygEdit->ConvertAfter($this->_content);
520 $this->_content = join("", $xml_output->_content);
522 $this->meta['pagetype'] = 'html';
524 return $this->_content;
526 return $this->_content;
530 function getLockedMessage () {
532 HTML(HTML::h2(_("Page Locked")),
533 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
534 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
535 HTML::p(_("Sorry for the inconvenience.")));
538 function isModerated() {
539 return $this->page->get('moderation');
541 function getModeratedMessage() {
543 HTML(HTML::h2(WikiLink(_("ModeratedPage"))),
544 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")))),
545 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.",
546 WikiLink(_("UserPreferences")))));
548 function getConflictMessage ($unresolved = false) {
550 xgettext only knows about c/c++ line-continuation strings
551 it does not know about php's dot operator.
552 We want to translate this entire paragraph as one string, of course.
555 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
558 $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.",
559 "<<<<<<< ". _("Your version"),
560 ">>>>>>> ". _("Other version")));
562 $message = HTML::p(_("Please check it through before saving."));
566 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
567 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
569 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
570 HTML::li(_("Save your updated changes.")));
573 HTML(HTML::h2(_("Conflicting Edits!")),
574 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
575 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.")),
580 function getTextArea () {
581 $request = &$this->request;
583 $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
585 // WYSIWYG will need two pagetypes: raw wikitest and converted html
586 if (ENABLE_WYSIWYG) {
587 $this->_wikicontent = $this->_content;
588 $this->_content = $this->WysiwygEdit->ConvertBefore($this->_content);
589 // $this->getPreview();
590 //$this->_htmlcontent = $this->_content->asXML();
593 $textarea = HTML::textarea(array('class'=> 'wikiedit',
594 'name' => 'edit[content]',
595 'id' => 'edit-content',
596 'rows' => $request->getPref('editHeight'),
597 'cols' => $request->getPref('editWidth'),
598 'readonly' => (bool) $readonly),
600 /** <textarea wrap="virtual"> is not valid XHTML but Netscape 4 requires it
601 * to wrap long lines.
604 $textarea->setAttr('wrap', 'virtual');
605 if (ENABLE_WYSIWYG) {
606 return $this->WysiwygEdit->Textarea($textarea, $this->_wikicontent,
607 $textarea->getAttr('name'));
612 function getFormElements () {
614 $request = &$this->request;
615 $page = &$this->page;
617 $h = array('action' => 'edit',
618 'pagename' => $page->getName(),
619 'version' => $this->version,
620 'edit[pagetype]' => $this->meta['pagetype'],
621 'edit[current_version]' => $this->_currentVersion);
623 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
624 $el['EDIT_TEXTAREA'] = $this->getTextArea();
625 if ( ENABLE_CAPTCHA ) {
626 $el = array_merge($el, $this->Captcha->getFormElements());
629 = HTML::input(array('type' => 'text',
630 'class' => 'wikitext',
631 'id' => 'edit-summary',
632 'name' => 'edit[summary]',
635 'value' => $this->meta['summary']));
637 = HTML::input(array('type' => 'checkbox',
638 'name' => 'edit[minor_edit]',
639 'id' => 'edit-minor_edit',
640 'checked' => (bool) $this->meta['is_minor_edit']));
642 = HTML::input(array('type' => 'checkbox',
643 'name' => 'edit[markup]',
645 'checked' => $this->meta['markup'] < 2.0,
646 'id' => 'useOldMarkup',
647 'onclick' => 'showOldMarkupRules(this.checked)'));
648 $el['OLD_MARKUP_CONVERT'] = ($this->meta['markup'] < 2.0)
649 ? Button('submit:edit[edit_convert]', _("Convert"), 'wikiaction') : '';
651 = HTML::input(array('type' => 'checkbox',
652 'name' => 'edit[locked]',
653 'id' => 'edit-locked',
654 'disabled' => (bool) !$this->user->isadmin(),
655 'checked' => (bool) $this->locked));
657 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
659 array('accesskey'=> 'p',
660 'title' => 'Preview the current content [alt-p]'));
662 //if (!$this->isConcurrentUpdate() && $this->canEdit())
663 $el['SAVE_B'] = Button('submit:edit[save]',
664 _("Save"), 'wikiaction',
665 array('accesskey'=> 's',
666 'title' => 'Save the current content as wikipage [alt-s]'));
667 $el['CHANGES_B'] = Button('submit:edit[diff]',
668 _("Changes"), 'wikiaction',
669 array('accesskey'=> 'c',
670 'title' => 'Preview the current changes as diff [alt-c]'));
671 $el['UPLOAD_B'] = Button('submit:edit[upload]',
672 _("Upload"), 'wikiaction',
673 array('title' => 'Select a local file and press Upload to attach into this page'));
674 $el['SPELLCHECK_B'] = Button('submit:edit[SpellCheck]',
675 _("Spell Check"), 'wikiaction',
676 array('title' => 'Check the spelling'));
677 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
680 = HTML::input(array('type' => 'text',
683 'class' => "numeric",
684 'name' => 'pref[editWidth]',
685 'id' => 'pref-editWidth',
686 'value' => $request->getPref('editWidth'),
687 'onchange' => 'this.form.submit();'));
689 = HTML::input(array('type' => 'text',
692 'class' => "numeric",
693 'name' => 'pref[editHeight]',
694 'id' => 'pref-editHeight',
695 'value' => $request->getPref('editHeight'),
696 'onchange' => 'this.form.submit();'));
697 $el['SEP'] = $WikiTheme->getButtonSeparator();
698 $el['AUTHOR_MESSAGE'] = fmt("Author will be logged as %s.",
699 HTML::em($this->user->getId()));
704 function _redirectToBrowsePage() {
705 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
708 function _restoreState () {
709 $request = &$this->request;
711 $posted = $request->getArg('edit');
712 $request->setArg('edit', false);
715 || !$request->isPost()
716 || !in_array($request->getArg('action'),array('edit','loadfile')))
719 if (!isset($posted['content']) || !is_string($posted['content']))
721 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
722 rtrim($posted['content']));
723 $this->_content = $this->getContent();
725 $this->_currentVersion = (int) $posted['current_version'];
727 if ($this->_currentVersion < 0)
729 if ($this->_currentVersion > $this->current->getVersion())
730 return false; // FIXME: some kind of warning?
732 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
733 $meta['markup'] = $is_old_markup ? false : 2.0;
734 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
735 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
736 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
737 if ( ENABLE_CAPTCHA )
738 $meta['captcha_input'] = !empty($posted['captcha_input']) ?
739 $posted['captcha_input'] : '';
741 $this->meta = array_merge($this->meta, $meta);
742 $this->locked = !empty($posted['locked']);
744 foreach (array('preview','save','edit_convert',
745 'keep_old','overwrite','diff','upload') as $o)
747 if (!empty($posted[$o]))
748 $this->editaction = $o;
750 if (empty($this->editaction))
751 $this->editaction = 'edit';
756 function _initializeState () {
757 $request = &$this->request;
758 $current = &$this->current;
759 $selected = &$this->selected;
760 $user = &$this->user;
763 NoSuchRevision($request, $this->page, $this->version); // noreturn
765 $this->_currentVersion = $current->getVersion();
766 $this->_content = $selected->getPackedContent();
768 $this->locked = $this->page->get('locked');
770 // If author same as previous author, default minor_edit to on.
771 $age = $this->meta['mtime'] - $current->get('mtime');
772 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
773 && $current->get('author') == $user->getId()
776 // Default for new pages is new-style markup.
777 if ($selected->hasDefaultContents())
778 $is_new_markup = true;
780 $is_new_markup = $selected->get('markup') >= 2.0;
782 $this->meta['markup'] = $is_new_markup ? 2.0: false;
783 $this->meta['pagetype'] = $selected->get('pagetype');
784 if ($this->meta['pagetype'] == 'wikiblog')
785 $this->meta['summary'] = $selected->get('summary'); // keep blog title
787 $this->meta['summary'] = '';
788 $this->editaction = 'edit';
792 class LoadFileConflictPageEditor
795 function editPage ($saveFailed = true) {
796 $tokens = &$this->tokens;
798 if (!$this->canEdit()) {
799 if ($this->isInitialEdit()) {
800 return $this->viewSource();
802 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
804 elseif ($this->editaction == 'save') {
805 if ($this->savePage()) {
806 return true; // Page saved.
811 if ($saveFailed || $this->isConcurrentUpdate())
813 // Get the text of the original page, and the two conflicting edits
814 // The diff class takes arrays as input. So retrieve content as
815 // an array, or convert it as necesary.
816 $orig = $this->page->getRevision($this->_currentVersion);
817 $this_content = explode("\n", $this->_content);
818 $other_content = $this->current->getContent();
819 require_once("lib/diff.php");
820 $diff2 = new Diff($other_content, $this_content);
821 $context_lines = max(4, count($other_content) + 1,
822 count($this_content) + 1);
823 $fmt = new BlockDiffFormatter($context_lines);
825 $this->_content = $fmt->format($diff2);
826 // FIXME: integrate this into class BlockDiffFormatter
827 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
829 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
832 $this->_currentVersion = $this->current->getVersion();
833 $this->version = $this->_currentVersion;
834 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
837 if ($this->editaction == 'edit_convert')
838 $tokens['PREVIEW_CONTENT'] = $this->getConvertedPreview();
839 if ($this->editaction == 'preview')
840 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
842 // FIXME: NOT_CURRENT_MESSAGE?
843 $tokens = array_merge($tokens, $this->getFormElements());
844 // we need all GET params for loadfile overwrite
845 if ($this->request->getArg('action') == 'loadfile') {
847 $this->tokens['HIDDEN_INPUTS'] =
849 (array('source' => $this->request->getArg('source'),
851 $this->tokens['HIDDEN_INPUTS']);
852 // add two conflict resolution buttons before preview and save.
853 $tokens['PREVIEW_B'] = HTML(
854 Button('submit:edit[keep_old]',
855 _("Keep old"), 'wikiaction'),
857 Button('submit:edit[overwrite]',
858 _("Overwrite with new"), 'wikiaction'),
860 $tokens['PREVIEW_B']);
862 if (ENABLE_EDIT_TOOLBAR and !ENABLE_WYSIWYG) {
863 include_once("lib/EditToolbar.php");
864 $toolbar = new EditToolbar();
865 $tokens = array_merge($tokens, $toolbar->getTokens());
868 return $this->output('editpage', _("Merge and Edit: %s"));
871 function output ($template, $title_fs) {
872 $selected = &$this->selected;
873 $current = &$this->current;
875 if ($selected && $selected->getVersion() != $current->getVersion()) {
877 $pagelink = WikiLink($selected);
881 $pagelink = WikiLink($this->page);
884 $title = new FormattedText ($title_fs, $pagelink);
885 $this->tokens['HEADER'] = $title;
886 //hack! there's no TITLE in editpage, but in the previous top template
887 if (empty($this->tokens['PAGE_LOCKED_MESSAGE']))
888 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML::h3($title);
890 $this->tokens['PAGE_LOCKED_MESSAGE'] = HTML(HTML::h3($title),
891 $this->tokens['PAGE_LOCKED_MESSAGE']);
892 $template = Template($template, $this->tokens);
894 //GeneratePage($template, $title, $rev);
899 function getConflictMessage () {
900 $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.",
903 HTML::p(_("Please check it through before saving."))));
909 $Log: not supported by cvs2svn $
910 Revision 1.112 2007/06/09 20:05:35 rurban
911 fix and optimize ENABLE_SPAMBLOCKLIST and ($newlinks > 5))
913 Revision 1.111 2007/06/03 17:12:00 rurban
914 convenience: only check above 5 external links for blocked domains
916 Revision 1.110 2007/01/07 18:42:00 rurban
917 Print ModeratedPage message on edit. Use GOOGLE_LINKS_NOFOLLOW. Improve id: edit: to edit-
919 Revision 1.109 2007/01/02 13:21:39 rurban
920 add two merge conflict buttons within loadfile: "Keep Old" and "Overwrite with new". enable edit toolbar there also. fix display of the Merge and Edit header.
922 Revision 1.108 2006/12/22 17:47:34 rurban
923 Display Warnings only once.
924 Add button accesskeys
926 Revision 1.107 2006/05/13 19:59:54 rurban
927 added wysiwyg_editor-1.3a feature by Jean-Nicolas GEREONE <jean-nicolas.gereone@st.com>
928 converted wysiwyg_editor-1.3a js to WysiwygEdit framework
929 changed default ENABLE_WYSIWYG = true and added WYSIWYG_BACKEND = Wikiwyg
931 Revision 1.106 2005/11/21 22:03:08 rurban
932 fix syntax error inside ENABLE_SPAMBLOCKLIST
934 Revision 1.105 2005/11/21 20:53:59 rurban
935 beautify request pref lines, no antispam if admin (netznetz request), user is a member anyway
937 Revision 1.104 2005/10/31 17:20:40 rurban
940 Revision 1.103 2005/10/31 17:09:13 rurban
941 use better constant WYSIWYG_DEFAULT_PAGETYPE_HTML
943 Revision 1.102 2005/10/31 16:47:14 rurban
944 enable wysiwyg html converters
946 Revision 1.101 2005/10/30 16:12:28 rurban
947 simplify viewsource tokens
949 Revision 1.100 2005/10/30 14:20:42 rurban
950 move Captcha specific vars and methods into a Captcha object
951 randomize Captcha chars positions and angles (smoothly)
953 Revision 1.99 2005/10/29 08:21:58 rurban
954 ENABLE_SPAMBLOCKLIST:
955 Check for links to blocked external tld domains in new edits, against
956 multi.surbl.org and bl.spamcop.net.
958 Revision 1.98 2005/10/10 19:37:04 rurban
959 change USE_HTMLAREA to ENABLE WYSIWYG, add NUM_SPAM_LINKS=20
961 Revision 1.97 2005/09/26 06:32:22 rurban
962 [] is forbidden in id tags. Renamed to use :
964 Revision 1.96 2005/05/06 17:54:22 rurban
965 silence Preview warnings for PAGE_LOCKED_MESSAGE, CONCURRENT_UPDATE_MESSAGE (thanks to schorni)
967 Revision 1.95 2005/04/25 20:17:14 rurban
968 captcha feature by Benjamin Drieu. Patch #1110699
970 Revision 1.94 2005/02/28 20:23:31 rurban
973 Revision 1.93 2005/02/27 19:31:52 rurban
974 hack: display errorstack without sideeffects (save and restore)
976 Revision 1.92 2005/01/29 20:37:21 rurban
977 no edit toolbar at all if ENABLE_EDITTOOLBAR = false
979 Revision 1.91 2005/01/25 07:05:49 rurban
980 extract toolbar code, support new tags to get rid of php inside templates
982 Revision 1.90 2005/01/22 12:46:15 rurban
983 fix oldmakrup button label
984 update pref[edit*] settings
986 Revision 1.89 2005/01/21 14:07:49 rurban
989 Revision 1.88 2004/12/17 16:39:03 rurban
992 Revision 1.87 2004/12/16 18:28:05 rurban
993 keep wikiblog summary = page title
995 Revision 1.86 2004/12/11 14:50:15 rurban
996 new edit_convert button, to get rid of old markup eventually
998 Revision 1.85 2004/12/06 19:49:56 rurban
999 enable action=remove which is undoable and seeable in RecentChanges: ADODB ony for now.
1000 renamed delete_page to purge_page.
1001 enable action=edit&version=-1 to force creation of a new version.
1002 added BABYCART_PATH config
1003 fixed magiqc in adodb.inc.php
1006 Revision 1.84 2004/12/04 12:58:26 rurban
1007 enable babycart Blog::SpamAssassin module on ENABLE_SPAMASSASSIN=true
1008 (currently only for php >= 4.3.0)
1010 Revision 1.83 2004/12/04 11:55:39 rurban
1011 First simple AntiSpam prevention:
1012 No more than 20 new http:// links allowed
1014 Revision 1.82 2004/11/30 22:21:56 rurban
1015 changed gif to optimized (pngout) png
1017 Revision 1.81 2004/11/29 17:57:27 rurban
1018 translated pulldown buttons
1020 Revision 1.80 2004/11/25 17:20:51 rurban
1021 and again a couple of more native db args: backlinks
1023 Revision 1.79 2004/11/21 11:59:20 rurban
1024 remove final \n to be ob_cache independent
1026 Revision 1.78 2004/11/16 17:57:45 rurban
1027 fix search&replace button
1028 use new addTagButton machinery
1029 new showPulldown for categories, TODO: in a seperate request
1031 Revision 1.77 2004/11/15 15:52:35 rurban
1032 improve js stability
1034 Revision 1.76 2004/11/15 15:37:34 rurban
1035 fix JS_SEARCHREPLACE
1036 don't use document.write for replace, otherwise self.opener is not defined.
1038 Revision 1.75 2004/09/16 08:00:52 rurban
1041 Revision 1.74 2004/07/03 07:36:28 rurban
1042 do not get unneccessary content
1044 Revision 1.73 2004/06/16 21:23:44 rurban
1045 fixed non-object fatal #215
1047 Revision 1.72 2004/06/14 11:31:37 rurban
1048 renamed global $Theme to $WikiTheme (gforge nameclash)
1049 inherit PageList default options from PageList
1050 default sortby=pagename
1051 use options in PageList_Selectable (limit, sortby, ...)
1052 added action revert, with button at action=diff
1053 added option regex to WikiAdminSearchReplace
1055 Revision 1.71 2004/06/03 18:06:29 rurban
1056 fix file locking issues (only needed on write)
1057 fixed immediate LANG and THEME in-session updates if not stored in prefs
1058 advanced editpage toolbars (search & replace broken)
1060 Revision 1.70 2004/06/02 20:47:47 rurban
1061 dont use the wikiaction class
1063 Revision 1.69 2004/06/02 10:17:56 rurban
1064 integrated search/replace into toolbar
1065 added save+preview buttons
1067 Revision 1.68 2004/06/01 15:28:00 rurban
1068 AdminUser only ADMIN_USER not member of Administrators
1069 some RateIt improvements by dfrankow
1070 edit_toolbar buttons
1072 Revision _1.6 2004/05/26 15:48:00 syilek
1073 fixed problem with creating page with slashes from one true page
1075 Revision _1.5 2004/05/25 16:51:53 syilek
1076 added ability to create a page from the category page and not have to edit it
1078 Revision 1.67 2004/05/27 17:49:06 rurban
1079 renamed DB_Session to DbSession (in CVS also)
1080 added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
1081 remove leading slash in error message
1082 added force_unlock parameter to File_Passwd (no return on stale locks)
1083 fixed adodb session AffectedRows
1084 added FileFinder helpers to unify local filenames and DATA_PATH names
1085 editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
1087 Revision 1.66 2004/04/29 23:25:12 rurban
1088 re-ordered locale init (as in 1.3.9)
1089 fixed loadfile with subpages, and merge/restore anyway
1090 (sf.net bug #844188)
1092 Revision 1.65 2004/04/18 01:11:52 rurban
1093 more numeric pagename fixes.
1094 fixed action=upload with merge conflict warnings.
1095 charset changed from constant to global (dynamic utf-8 switching)
1097 Revision 1.64 2004/04/06 19:48:56 rurban
1098 temp workaround for action=edit AddComment form
1100 Revision 1.63 2004/03/24 19:39:02 rurban
1101 php5 workaround code (plus some interim debugging code in XmlElement)
1102 php5 doesn't work yet with the current XmlElement class constructors,
1103 WikiUserNew does work better than php4.
1104 rewrote WikiUserNew user upgrading to ease php5 update
1105 fixed pref handling in WikiUserNew
1106 added Email Notification
1107 added simple Email verification
1108 removed emailVerify userpref subclass: just a email property
1109 changed pref binary storage layout: numarray => hash of non default values
1110 print optimize message only if really done.
1111 forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string.
1112 prefs should be stored in db or homepage, besides the current session.
1114 Revision 1.62 2004/03/17 18:41:05 rurban
1115 initial_content and template support for CreatePage
1117 Revision 1.61 2004/03/12 20:59:17 rurban
1118 important cookie fix by Konstantin Zadorozhny
1119 new editpage feature: JS_SEARCHREPLACE
1121 Revision 1.60 2004/02/15 21:34:37 rurban
1122 PageList enhanced and improved.
1123 fixed new WikiAdmin... plugins
1124 editpage, Theme with exp. htmlarea framework
1125 (htmlarea yet committed, this is really questionable)
1126 WikiUser... code with better session handling for prefs
1127 enhanced UserPreferences (again)
1128 RecentChanges for show_deleted: how should pages be deleted then?
1130 Revision 1.59 2003/12/07 20:35:26 carstenklapp
1131 Bugfix: Concurrent updates broken since after 1.3.4 release: Fatal
1132 error: Call to undefined function: gettransformedcontent() in
1133 /home/groups/p/ph/phpwiki/htdocs/phpwiki2/lib/editpage.php on line
1136 Revision 1.58 2003/03/10 18:25:22 dairiki
1137 Bug/typo fix. If you use the edit page to un/lock a page, it
1138 failed with: Fatal error: Call to a member function on a
1139 non-object in editpage.php on line 136
1141 Revision 1.57 2003/02/26 03:40:22 dairiki
1142 New action=create. Essentially the same as action=edit, except that if the
1143 page already exists, it falls back to action=browse.
1145 This is for use in the "question mark" links for unknown wiki words
1146 to avoid problems and confusion when following links from stale pages.
1147 (If the "unknown page" has been created in the interim, the user probably
1148 wants to view the page before editing it.)
1150 Revision 1.56 2003/02/21 18:07:14 dairiki
1151 Minor, nitpicky, currently inconsequential changes.
1153 Revision 1.55 2003/02/21 04:10:58 dairiki
1154 Fixes for new cached markup.
1155 Some minor code cleanups.
1157 Revision 1.54 2003/02/16 19:47:16 dairiki
1158 Update WikiDB timestamp when editing or deleting pages.
1160 Revision 1.53 2003/02/15 23:20:27 dairiki
1161 Redirect back to browse current version of page upon save,
1162 even when no changes were made.
1164 Revision 1.52 2003/01/03 22:22:00 carstenklapp
1165 Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
1167 Revision 1.51 2003/01/03 02:43:26 carstenklapp
1168 New class LoadFileConflictPageEditor, for merging / comparing a loaded
1169 pgsrc file with an existing page.
1176 // c-basic-offset: 4
1177 // c-hanging-comment-ender-p: nil
1178 // indent-tabs-mode: nil