2 rcs_id('$Id: editpage.php,v 1.60 2004-02-15 21:34:37 rurban Exp $');
4 require_once('lib/Template.php');
6 // Not yet enabled, since we cannot convert HTML to Wiki Markup yet.
7 //Todo: change from constant to user preference variable. (or checkbox setting)
8 if (!defined('USE_HTMLAREA')) define('USE_HTMLAREA',false);
9 if (USE_HTMLAREA) require_once('lib/htmlarea.php');
13 function PageEditor (&$request) {
14 $this->request = &$request;
16 $this->user = $request->getUser();
17 $this->page = $request->getPage();
19 $this->current = $this->page->getCurrentRevision();
21 // HACKish short circuit to browse on action=create
22 if ($request->getArg('action') == 'create') {
23 if (! $this->current->hasDefaultContents())
24 $request->redirect(WikiURL($this->page->getName())); // noreturn
28 $this->meta = array('author' => $this->user->getId(),
29 'author_id' => $this->user->getAuthenticatedId(),
32 $this->tokens = array();
34 $version = $request->getArg('version');
35 if ($version !== false) {
36 $this->selected = $this->page->getRevision($version);
37 $this->version = $version;
40 $this->selected = $this->current;
41 $this->version = $this->current->getVersion();
44 if ($this->_restoreState()) {
45 $this->_initialEdit = false;
48 $this->_initializeState();
49 $this->_initialEdit = true;
52 header("Content-Type: text/html; charset=" . CHARSET);
55 function editPage () {
57 $tokens = &$this->tokens;
59 if (! $this->canEdit()) {
60 if ($this->isInitialEdit())
61 return $this->viewSource();
62 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
64 elseif ($this->editaction == 'save') {
65 if ($this->savePage())
66 return true; // Page saved.
70 if ($saveFailed || $this->isConcurrentUpdate())
72 // Get the text of the original page, and the two conflicting edits
73 // The diff3 class takes arrays as input. So retrieve content as
74 // an array, or convert it as necesary.
75 $orig = $this->page->getRevision($this->_currentVersion);
76 // FIXME: what if _currentVersion has be deleted?
77 $orig_content = $orig->getContent();
78 $this_content = explode("\n", $this->_content);
79 $other_content = $this->current->getContent();
80 include_once("lib/diff3.php");
81 $diff = new diff3($orig_content, $this_content, $other_content);
82 $output = $diff->merged_output(_("Your version"), _("Other version"));
83 // Set the content of the textarea to the merged diff
84 // output, and update the version
85 $this->_content = implode ("\n", $output);
86 $this->_currentVersion = $this->current->getVersion();
87 $this->version = $this->_currentVersion;
88 $unresolved = $diff->ConflictingBlocks;
89 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage($unresolved);
92 if ($this->editaction == 'preview')
93 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
95 // FIXME: NOT_CURRENT_MESSAGE?
97 $tokens = array_merge($tokens, $this->getFormElements());
99 return $this->output('editpage', _("Edit: %s"));
102 function output ($template, $title_fs) {
104 $selected = &$this->selected;
105 $current = &$this->current;
107 if ($selected && $selected->getVersion() != $current->getVersion()) {
109 $pagelink = WikiLink($selected);
113 $pagelink = WikiLink($this->page);
117 $title = new FormattedText ($title_fs, $pagelink);
118 if ($template == 'editpage' and USE_HTMLAREA) {
119 $Theme->addMoreHeaders(Edit_HtmlArea_Head());
120 //$tokens['PAGE_SOURCE'] = Edit_HtmlArea_ConvertBefore($this->_content);
122 $template = Template($template, $this->tokens);
123 GeneratePage($template, $title, $rev);
128 function viewSource () {
129 assert($this->isInitialEdit());
130 assert($this->selected);
132 $this->tokens['PAGE_SOURCE'] = $this->_content;
133 return $this->output('viewsource', _("View Source: %s"));
136 function updateLock() {
137 if ((bool)$this->page->get('locked') == (bool)$this->locked)
138 return false; // Not changed.
140 if (!$this->user->isAdmin()) {
141 // FIXME: some sort of message
142 return false; // not allowed.
145 $this->page->set('locked', (bool)$this->locked);
146 $this->tokens['LOCK_CHANGED_MSG']
147 = $this->locked ? _("Page now locked.") : _("Page now unlocked.");
149 return true; // lock changed.
152 function savePage () {
153 $request = &$this->request;
155 if ($this->isUnchanged()) {
156 // Allow admin lock/unlock even if
157 // no text changes were made.
158 if ($this->updateLock()) {
159 $dbi = $request->getDbh();
162 // Save failed. No changes made.
163 $this->_redirectToBrowsePage();
164 // user will probably not see the rest of this...
165 include_once('lib/display.php');
166 // force browse of current version:
167 $request->setArg('version', false);
168 displayPage($request, 'nochanges');
172 $page = &$this->page;
174 // Include any meta-data from original page version which
175 // has not been explicitly updated.
176 // (Except don't propagate pgsrc_version --- moot for now,
177 // because at present it never gets into the db...)
178 $meta = $this->selected->getMetaData();
179 unset($meta['pgsrc_version']);
180 $meta = array_merge($meta, $this->meta);
183 $this->_content = $this->getContent();
184 $newrevision = $page->save($this->_content, $this->_currentVersion + 1, $meta);
185 if (!isa($newrevision, 'wikidb_pagerevision')) {
186 // Save failed. (Concurrent updates).
190 // New contents successfully saved...
193 // Clean out archived versions of this page.
194 include_once('lib/ArchiveCleaner.php');
195 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
196 $cleaner->cleanPageRevisions($page);
198 $dbi = $request->getDbh();
199 $warnings = $dbi->GenericWarnings();
203 if (empty($warnings) && ! $Theme->getImageURL('signature')) {
204 // Do redirect to browse page if no signature has
205 // been defined. In this case, the user will most
206 // likely not see the rest of the HTML we generate
208 $this->_redirectToBrowsePage();
211 // Force browse of current page version.
212 $request->setArg('version', false);
214 $template = Template('savepage', $this->tokens);
215 $template->replace('CONTENT', $newrevision->getTransformedContent());
216 if (!empty($warnings))
217 $template->replace('WARNINGS', $warnings);
219 $pagelink = WikiLink($page);
221 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
225 function isConcurrentUpdate () {
226 assert($this->current->getVersion() >= $this->_currentVersion);
227 return $this->current->getVersion() != $this->_currentVersion;
230 function canEdit () {
231 return !$this->page->get('locked') || $this->user->isAdmin();
234 function isInitialEdit () {
235 return $this->_initialEdit;
238 function isUnchanged () {
239 $current = &$this->current;
241 if ($this->meta['markup'] != $current->get('markup'))
244 return $this->_content == $current->getPackedContent();
247 function getPreview () {
248 include_once('lib/PageType.php');
249 $this->_content = $this->getContent();
250 return new TransformedText($this->page, $this->_content, $this->meta);
253 // possibly convert HTMLAREA content back to Wiki markup
254 function getContent () {
256 $xml_output = Edit_HtmlArea_ConvertAfter($this->_content);
257 $this->_content = join("",$xml_output->_content);
258 return $this->_content;
260 return $this->_content;
264 function getLockedMessage () {
266 HTML(HTML::h2(_("Page Locked")),
267 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
268 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
269 HTML::p(_("Sorry for the inconvenience.")));
272 function getConflictMessage ($unresolved = false) {
274 xgettext only knows about c/c++ line-continuation strings
275 it does not know about php's dot operator.
276 We want to translate this entire paragraph as one string, of course.
279 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
282 $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.",
283 "<<<<<<< ". _("Your version"),
284 ">>>>>>> ". _("Other version")));
286 $message = HTML::p(_("Please check it through before saving."));
290 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
291 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
293 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
294 HTML::li(_("Save your updated changes.")));
297 HTML(HTML::h2(_("Conflicting Edits!")),
298 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
299 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.")),
304 function getTextArea () {
305 $request = &$this->request;
307 // wrap=virtual is not HTML4, but without it NS4 doesn't wrap
309 $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
311 $html = $this->getPreview();
312 $this->_wikicontent = $this->_content;
313 $this->_content = $html->asXML();
315 $textarea = HTML::textarea(array('class' => 'wikiedit',
316 'name' => 'edit[content]',
317 'id' => 'edit[content]',
318 'rows' => $request->getPref('editHeight'),
319 'cols' => $request->getPref('editWidth'),
320 'readonly' => (bool) $readonly,
321 'wrap' => 'virtual'),
324 return Edit_HtmlArea_Textarea($textarea,$this->_wikicontent,'edit[content]');
329 function getFormElements () {
330 $request = &$this->request;
331 $page = &$this->page;
334 $h = array('action' => 'edit',
335 'pagename' => $page->getName(),
336 'version' => $this->version,
337 'edit[pagetype]' => $this->meta['pagetype'],
338 'edit[current_version]' => $this->_currentVersion);
340 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
343 $el['EDIT_TEXTAREA'] = $this->getTextArea();
346 = HTML::input(array('type' => 'text',
347 'class' => 'wikitext',
348 'name' => 'edit[summary]',
351 'value' => $this->meta['summary']));
353 = HTML::input(array('type' => 'checkbox',
354 'name' => 'edit[minor_edit]',
355 'checked' => (bool) $this->meta['is_minor_edit']));
357 = HTML::input(array('type' => 'checkbox',
358 'name' => 'edit[markup]',
360 'checked' => $this->meta['markup'] < 2.0,
361 'id' => 'useOldMarkup',
362 'onclick' => 'showOldMarkupRules(this.checked)'));
365 = HTML::input(array('type' => 'checkbox',
366 'name' => 'edit[locked]',
367 'disabled' => (bool) !$this->user->isadmin(),
368 'checked' => (bool) $this->locked));
370 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
373 //if (!$this->isConcurrentUpdate() && $this->canEdit())
374 $el['SAVE_B'] = Button('submit:edit[save]', _("Save"), 'wikiaction');
376 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
381 function _redirectToBrowsePage() {
382 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
386 function _restoreState () {
387 $request = &$this->request;
389 $posted = $request->getArg('edit');
390 $request->setArg('edit', false);
392 if (!$posted || !$request->isPost()
393 || $request->getArg('action') != 'edit')
396 if (!isset($posted['content']) || !is_string($posted['content']))
398 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
399 rtrim($posted['content']));
400 $this->_content = $this->getContent();
402 $this->_currentVersion = (int) $posted['current_version'];
404 if ($this->_currentVersion < 0)
406 if ($this->_currentVersion > $this->current->getVersion())
407 return false; // FIXME: some kind of warning?
409 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
410 $meta['markup'] = $is_old_markup ? false : 2.0;
411 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
412 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
413 $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
414 $this->meta = array_merge($this->meta, $meta);
415 $this->locked = !empty($posted['locked']);
417 if (!empty($posted['preview']))
418 $this->editaction = 'preview';
419 elseif (!empty($posted['save']))
420 $this->editaction = 'save';
422 $this->editaction = 'edit';
427 function _initializeState () {
428 $request = &$this->request;
429 $current = &$this->current;
430 $selected = &$this->selected;
431 $user = &$this->user;
434 NoSuchRevision($request, $this->page, $this->version); // noreturn
436 $this->_currentVersion = $current->getVersion();
437 $this->_content = $selected->getPackedContent();
439 $this->meta['summary'] = '';
440 $this->locked = $this->page->get('locked');
442 // If author same as previous author, default minor_edit to on.
443 $age = $this->meta['mtime'] - $current->get('mtime');
444 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
445 && $current->get('author') == $user->getId()
448 // Default for new pages is new-style markup.
449 if ($selected->hasDefaultContents())
450 $is_new_markup = true;
452 $is_new_markup = $selected->get('markup') >= 2.0;
454 $this->meta['markup'] = $is_new_markup ? 2.0: false;
455 $this->meta['pagetype'] = $selected->get('pagetype');
456 $this->editaction = 'edit';
460 class LoadFileConflictPageEditor
463 function editPage ($saveFailed = true) {
464 $tokens = &$this->tokens;
466 if (!$this->canEdit()) {
467 if ($this->isInitialEdit())
468 return $this->viewSource();
469 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
471 elseif ($this->editaction == 'save') {
472 if ($this->savePage())
473 return true; // Page saved.
477 if ($saveFailed || $this->isConcurrentUpdate())
479 // Get the text of the original page, and the two conflicting edits
480 // The diff class takes arrays as input. So retrieve content as
481 // an array, or convert it as necesary.
482 $orig = $this->page->getRevision($this->_currentVersion);
483 $this_content = explode("\n", $this->_content);
484 $other_content = $this->current->getContent();
485 include_once("lib/diff.php");
486 $diff2 = new Diff($other_content, $this_content);
487 $context_lines = max(4, count($other_content) + 1,
488 count($this_content) + 1);
489 $fmt = new BlockDiffFormatter($context_lines);
491 $this->_content = $fmt->format($diff2);
492 // FIXME: integrate this into class BlockDiffFormatter
493 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
495 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
498 $this->_currentVersion = $this->current->getVersion();
499 $this->version = $this->_currentVersion;
500 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
503 if ($this->editaction == 'preview')
504 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
506 // FIXME: NOT_CURRENT_MESSAGE?
508 $tokens = array_merge($tokens, $this->getFormElements());
510 return $this->output('editpage', _("Merge and Edit: %s"));
511 // FIXME: this doesn't display
514 function output ($template, $title_fs) {
515 $selected = &$this->selected;
516 $current = &$this->current;
518 if ($selected && $selected->getVersion() != $current->getVersion()) {
520 $pagelink = WikiLink($selected);
524 $pagelink = WikiLink($this->page);
527 $title = new FormattedText ($title_fs, $pagelink);
528 $template = Template($template, $this->tokens);
530 //GeneratePage($template, $title, $rev);
534 function getConflictMessage () {
535 $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.",
538 HTML::p(_("Please check it through before saving."))));
544 $Log: not supported by cvs2svn $
545 Revision 1.59 2003/12/07 20:35:26 carstenklapp
546 Bugfix: Concurrent updates broken since after 1.3.4 release: Fatal
547 error: Call to undefined function: gettransformedcontent() in
548 /home/groups/p/ph/phpwiki/htdocs/phpwiki2/lib/editpage.php on line
551 Revision 1.58 2003/03/10 18:25:22 dairiki
552 Bug/typo fix. If you use the edit page to un/lock a page, it
553 failed with: Fatal error: Call to a member function on a
554 non-object in editpage.php on line 136
556 Revision 1.57 2003/02/26 03:40:22 dairiki
557 New action=create. Essentially the same as action=edit, except that if the
558 page already exists, it falls back to action=browse.
560 This is for use in the "question mark" links for unknown wiki words
561 to avoid problems and confusion when following links from stale pages.
562 (If the "unknown page" has been created in the interim, the user probably
563 wants to view the page before editing it.)
565 Revision 1.56 2003/02/21 18:07:14 dairiki
566 Minor, nitpicky, currently inconsequential changes.
568 Revision 1.55 2003/02/21 04:10:58 dairiki
569 Fixes for new cached markup.
570 Some minor code cleanups.
572 Revision 1.54 2003/02/16 19:47:16 dairiki
573 Update WikiDB timestamp when editing or deleting pages.
575 Revision 1.53 2003/02/15 23:20:27 dairiki
576 Redirect back to browse current version of page upon save,
577 even when no changes were made.
579 Revision 1.52 2003/01/03 22:22:00 carstenklapp
580 Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
582 Revision 1.51 2003/01/03 02:43:26 carstenklapp
583 New class LoadFileConflictPageEditor, for merging / comparing a loaded
584 pgsrc file with an existing page.
592 // c-hanging-comment-ender-p: nil
593 // indent-tabs-mode: nil