2 rcs_id('$Id: editpage.php,v 1.54 2003-02-16 19:47:16 dairiki 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();
16 $this->meta = array('author' => $this->user->getId(),
17 'locked' => $this->page->get('locked'),
18 'author_id' => $this->user->getAuthenticatedId());
20 $version = $request->getArg('version');
21 if ($version !== false) {
22 $this->selected = $this->page->getRevision($version);
23 $this->version = $version;
26 $this->selected = $this->current;
27 $this->version = $this->current->getVersion();
30 if ($this->_restoreState()) {
31 $this->_initialEdit = false;
34 $this->_initializeState();
35 $this->_initialEdit = true;
38 header("Content-Type: text/html; charset=" . CHARSET);
41 function editPage () {
45 if ($this->canEdit()) {
46 if ($this->isInitialEdit())
47 return $this->viewSource();
48 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
50 elseif ($this->editaction == 'save') {
51 if ($this->savePage())
52 return true; // Page saved.
56 if ($saveFailed || $this->isConcurrentUpdate())
58 // Get the text of the original page, and the two conflicting edits
59 // The diff3 class takes arrays as input. So retrieve content as
60 // an array, or convert it as necesary.
61 $orig = $this->page->getRevision($this->_currentVersion);
62 $orig_content = $orig->getContent();
63 $this_content = explode("\n", $this->_content);
64 $other_content = $this->current->getContent();
65 include_once("lib/diff3.php");
66 $diff = new diff3($orig_content, $this_content, $other_content);
67 $output = $diff->merged_output(_("Your version"), _("Other version"));
68 // Set the content of the textarea to the merged diff
69 // output, and update the version
70 $this->_content = implode ("\n", $output);
71 $this->_currentVersion = $this->current->getVersion();
72 $this->version = $this->_currentVersion;
73 $unresolved = $diff->ConflictingBlocks;
74 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage($unresolved);
77 if ($this->editaction == 'preview')
78 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
80 // FIXME: NOT_CURRENT_MESSAGE?
82 $tokens = array_merge($tokens, $this->getFormElements());
84 return $this->output('editpage', _("Edit: %s"), $tokens);
87 function output ($template, $title_fs, $tokens) {
88 $selected = &$this->selected;
89 $current = &$this->current;
91 if ($selected && $selected->getVersion() != $current->getVersion()) {
93 $pagelink = WikiLink($selected);
97 $pagelink = WikiLink($this->page);
101 $title = new FormattedText ($title_fs, $pagelink);
102 $template = Template($template, $tokens);
104 GeneratePage($template, $title, $rev);
109 function viewSource ($tokens = false) {
110 assert($this->isInitialEdit());
111 assert($this->selected);
113 $tokens['PAGE_SOURCE'] = $this->_content;
115 return $this->output('viewsource', _("View Source: %s"), $tokens);
118 function setPageLockChanged($isadmin, $lock, &$page) {
120 if (! $page->get('locked') == $lock) {
121 $request = &$this->request;
122 $request->setArg('lockchanged', true); //is it safe to add new args to $request like this?
124 $page->set('locked', $lock);
128 function savePage () {
129 $request = &$this->request;
131 if ($this->isUnchanged()) {
132 // Allow admin lock/unlock even if
133 // no text changes were made.
134 if ($isadmin = $this->user->isadmin()) {
135 $page = &$this->page;
136 $lock = $this->meta['locked'];
137 $this->setPageLockChanged($isadmin, $lock, $page);
138 $dbi = $request->getDbh();
141 // Save failed. No changes made.
142 $this->_redirectToBrowsePage();
143 // user will probably not see the rest of this...
144 include_once('lib/display.php');
145 // force browse of current version:
146 $request->setArg('version', false);
147 displayPage($request, 'nochanges');
151 $page = &$this->page;
152 $lock = $this->meta['locked'];
153 $this->meta['locked'] = ''; // hackish
156 $newrevision = $page->createRevision($this->_currentVersion + 1,
159 ExtractWikiPageLinks($this->_content));
160 if (!is_object($newrevision)) {
161 // Save failed. (Concurrent updates).
164 // New contents successfully saved...
165 if ($isadmin = $this->user->isadmin())
166 $this->setPageLockChanged($isadmin, $lock, $page);
168 // Clean out archived versions of this page.
169 include_once('lib/ArchiveCleaner.php');
170 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
171 $cleaner->cleanPageRevisions($page);
173 $dbi = $request->getDbh();
174 $warnings = $dbi->GenericWarnings();
178 if (empty($warnings) && ! $Theme->getImageURL('signature')) {
179 // Do redirect to browse page if no signature has
180 // been defined. In this case, the user will most
181 // likely not see the rest of the HTML we generate
183 $this->_redirectToBrowsePage();
186 // Force browse of current page version.
187 $request->setArg('version', false);
189 require_once('lib/PageType.php');
190 $transformedText = PageType($newrevision);
191 $template = Template('savepage',
192 array('CONTENT' => $transformedText));
193 //include_once('lib/BlockParser.php');
194 //$template = Template('savepage',
195 // array('CONTENT' => TransformText($newrevision)));
196 if (!empty($warnings))
197 $template->replace('WARNINGS', $warnings);
199 $pagelink = WikiLink($page);
201 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
205 function isConcurrentUpdate () {
206 assert($this->current->getVersion() >= $this->_currentVersion);
207 return $this->current->getVersion() != $this->_currentVersion;
210 function canEdit () {
211 return $this->page->get('locked') && !$this->user->isAdmin();
214 function isInitialEdit () {
215 return $this->_initialEdit;
218 function isUnchanged () {
219 $current = &$this->current;
221 if ($this->meta['markup'] != $current->get('markup'))
224 return $this->_content == $current->getPackedContent();
227 function getPreview () {
228 require_once('lib/PageType.php');
229 return PageType($this->_content, $this->page->getName(), $this->meta['markup']);
231 //include_once('lib/BlockParser.php');
232 //return TransformText($this->_content, $this->meta['markup']);
235 function getLockedMessage () {
237 HTML(HTML::h2(_("Page Locked")),
238 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
239 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
240 HTML::p(_("Sorry for the inconvenience.")));
243 function getConflictMessage ($unresolved = false) {
245 xgettext only knows about c/c++ line-continuation strings
246 it does not know about php's dot operator.
247 We want to translate this entire paragraph as one string, of course.
250 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
253 $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.",
254 "<<<<<<< ". _("Your version"),
255 ">>>>>>> ". _("Other version")));
257 $message = HTML::p(_("Please check it through before saving."));
261 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
262 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
264 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
265 HTML::li(_("Save your updated changes.")));
268 HTML(HTML::h2(_("Conflicting Edits!")),
269 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
270 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.")),
275 function getTextArea () {
276 $request = &$this->request;
278 // wrap=virtual is not HTML4, but without it NS4 doesn't wrap
280 $readonly = $this->canEdit(); // || $this->isConcurrentUpdate();
282 return HTML::textarea(array('class' => 'wikiedit',
283 'name' => 'edit[content]',
284 'rows' => $request->getPref('editHeight'),
285 'cols' => $request->getPref('editWidth'),
286 'readonly' => (bool) $readonly,
287 'wrap' => 'virtual'),
291 function getFormElements () {
292 $request = &$this->request;
293 $page = &$this->page;
296 $h = array('action' => 'edit',
297 'pagename' => $page->getName(),
298 'version' => $this->version,
299 'edit[current_version]' => $this->_currentVersion);
301 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
304 $el['EDIT_TEXTAREA'] = $this->getTextArea();
307 = HTML::input(array('type' => 'text',
308 'class' => 'wikitext',
309 'name' => 'edit[summary]',
312 'value' => $this->meta['summary']));
314 = HTML::input(array('type' => 'checkbox',
315 'name' => 'edit[minor_edit]',
316 'checked' => (bool) $this->meta['is_minor_edit']));
318 = HTML::input(array('type' => 'checkbox',
319 'name' => 'edit[markup]',
321 'checked' => $this->meta['markup'] < 2.0,
322 'id' => 'useOldMarkup',
323 'onclick' => 'showOldMarkupRules(this.checked)'));
326 = HTML::input(array('type' => 'checkbox',
327 'name' => 'edit[locked]',
328 'disabled' => (bool) !$this->user->isadmin(),
329 'checked' => (bool) $this->meta['locked']));
331 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
334 //if (!$this->isConcurrentUpdate() && !$this->canEdit())
335 $el['SAVE_B'] = Button('submit:edit[save]', _("Save"), 'wikiaction');
337 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
342 function _redirectToBrowsePage() {
343 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
347 function _restoreState () {
348 $request = &$this->request;
350 $posted = $request->getArg('edit');
351 $request->setArg('edit', false);
353 if (!$posted || !$request->isPost()
354 || $request->getArg('action') != 'edit')
357 if (!isset($posted['content']) || !is_string($posted['content']))
359 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
360 rtrim($posted['content']));
362 $this->_currentVersion = (int) $posted['current_version'];
364 if ($this->_currentVersion < 0)
366 if ($this->_currentVersion > $this->current->getVersion())
367 return false; // FIXME: some kind of warning?
369 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
370 $meta['markup'] = $is_old_markup ? false : 2.0;
371 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
372 $meta['locked'] = !empty($posted['locked']);
373 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
375 $this->meta = array_merge($this->meta, $meta);
377 if (!empty($posted['preview']))
378 $this->editaction = 'preview';
379 elseif (!empty($posted['save']))
380 $this->editaction = 'save';
382 $this->editaction = 'edit';
387 function _initializeState () {
388 $request = &$this->request;
389 $current = &$this->current;
390 $selected = &$this->selected;
391 $user = &$this->user;
394 NoSuchRevision($request, $this->page, $this->version); // noreturn
396 $this->_currentVersion = $current->getVersion();
397 $this->_content = $selected->getPackedContent();
399 $this->meta['summary'] = '';
400 $this->meta['locked'] = $this->page->get('locked');
402 // If author same as previous author, default minor_edit to on.
403 $age = time() - $current->get('mtime');
404 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
405 && $current->get('author') == $user->getId()
408 // Default for new pages is new-style markup.
409 if ($selected->hasDefaultContents())
410 $is_new_markup = true;
412 $is_new_markup = $selected->get('markup') >= 2.0;
414 $this->meta['markup'] = $is_new_markup ? 2.0: false;
415 $this->editaction = 'edit';
419 class LoadFileConflictPageEditor
422 function editPage ($saveFailed = true) {
425 if ($this->canEdit()) {
426 if ($this->isInitialEdit())
427 return $this->viewSource();
428 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
430 elseif ($this->editaction == 'save') {
431 if ($this->savePage())
432 return true; // Page saved.
436 if ($saveFailed || $this->isConcurrentUpdate())
438 // Get the text of the original page, and the two conflicting edits
439 // The diff class takes arrays as input. So retrieve content as
440 // an array, or convert it as necesary.
441 $orig = $this->page->getRevision($this->_currentVersion);
442 $this_content = explode("\n", $this->_content);
443 $other_content = $this->current->getContent();
444 include_once("lib/diff.php");
445 $diff2 = new Diff($other_content, $this_content);
446 $context_lines = max(4, count($other_content) + 1,
447 count($this_content) + 1);
448 $fmt = new BlockDiffFormatter($context_lines);
450 $this->_content = $fmt->format($diff2);
451 // FIXME: integrate this into class BlockDiffFormatter
452 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
454 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
457 $this->_currentVersion = $this->current->getVersion();
458 $this->version = $this->_currentVersion;
459 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
462 if ($this->editaction == 'preview')
463 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
465 // FIXME: NOT_CURRENT_MESSAGE?
467 $tokens = array_merge($tokens, $this->getFormElements());
469 return $this->output('editpage', _("Merge and Edit: %s"), $tokens);
470 // FIXME: this doesn't display
473 function output ($template, $title_fs, $tokens) {
474 $selected = &$this->selected;
475 $current = &$this->current;
477 if ($selected && $selected->getVersion() != $current->getVersion()) {
479 $pagelink = WikiLink($selected);
483 $pagelink = WikiLink($this->page);
486 $title = new FormattedText ($title_fs, $pagelink);
487 $template = Template($template, $tokens);
489 //GeneratePage($template, $title, $rev);
493 function getConflictMessage () {
494 $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.",
497 HTML::p(_("Please check it through before saving."))));
503 $Log: not supported by cvs2svn $
504 Revision 1.53 2003/02/15 23:20:27 dairiki
505 Redirect back to browse current version of page upon save,
506 even when no changes were made.
508 Revision 1.52 2003/01/03 22:22:00 carstenklapp
509 Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
511 Revision 1.51 2003/01/03 02:43:26 carstenklapp
512 New class LoadFileConflictPageEditor, for merging / comparing a loaded
513 pgsrc file with an existing page.
521 // c-hanging-comment-ender-p: nil
522 // indent-tabs-mode: nil