2 rcs_id('$Id: editpage.php,v 1.53 2003-02-15 23:20:27 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);
139 // Save failed. No changes made.
140 $this->_redirectToBrowsePage();
141 // user will probably not see the rest of this...
142 include_once('lib/display.php');
143 // force browse of current version:
144 $request->setArg('version', false);
145 displayPage($request, 'nochanges');
149 $page = &$this->page;
150 $lock = $this->meta['locked'];
151 $this->meta['locked'] = ''; // hackish
154 $newrevision = $page->createRevision($this->_currentVersion + 1,
157 ExtractWikiPageLinks($this->_content));
158 if (!is_object($newrevision)) {
159 // Save failed. (Concurrent updates).
162 // New contents successfully saved...
163 if ($isadmin = $this->user->isadmin())
164 $this->setPageLockChanged($isadmin, $lock, $page);
166 // Clean out archived versions of this page.
167 include_once('lib/ArchiveCleaner.php');
168 $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
169 $cleaner->cleanPageRevisions($page);
171 $dbi = $request->getDbh();
172 $warnings = $dbi->GenericWarnings();
175 if (empty($warnings) && ! $Theme->getImageURL('signature')) {
176 // Do redirect to browse page if no signature has
177 // been defined. In this case, the user will most
178 // likely not see the rest of the HTML we generate
180 $this->_redirectToBrowsePage();
183 // Force browse of current page version.
184 $request->setArg('version', false);
186 require_once('lib/PageType.php');
187 $transformedText = PageType($newrevision);
188 $template = Template('savepage',
189 array('CONTENT' => $transformedText));
190 //include_once('lib/BlockParser.php');
191 //$template = Template('savepage',
192 // array('CONTENT' => TransformText($newrevision)));
193 if (!empty($warnings))
194 $template->replace('WARNINGS', $warnings);
196 $pagelink = WikiLink($page);
198 GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
202 function isConcurrentUpdate () {
203 assert($this->current->getVersion() >= $this->_currentVersion);
204 return $this->current->getVersion() != $this->_currentVersion;
207 function canEdit () {
208 return $this->page->get('locked') && !$this->user->isAdmin();
211 function isInitialEdit () {
212 return $this->_initialEdit;
215 function isUnchanged () {
216 $current = &$this->current;
218 if ($this->meta['markup'] != $current->get('markup'))
221 return $this->_content == $current->getPackedContent();
224 function getPreview () {
225 require_once('lib/PageType.php');
226 return PageType($this->_content, $this->page->getName(), $this->meta['markup']);
228 //include_once('lib/BlockParser.php');
229 //return TransformText($this->_content, $this->meta['markup']);
232 function getLockedMessage () {
234 HTML(HTML::h2(_("Page Locked")),
235 HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
236 HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
237 HTML::p(_("Sorry for the inconvenience.")));
240 function getConflictMessage ($unresolved = false) {
242 xgettext only knows about c/c++ line-continuation strings
243 it does not know about php's dot operator.
244 We want to translate this entire paragraph as one string, of course.
247 //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
250 $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.",
251 "<<<<<<< ". _("Your version"),
252 ">>>>>>> ". _("Other version")));
254 $message = HTML::p(_("Please check it through before saving."));
258 /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
259 HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
261 HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
262 HTML::li(_("Save your updated changes.")));
265 HTML(HTML::h2(_("Conflicting Edits!")),
266 HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
267 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.")),
272 function getTextArea () {
273 $request = &$this->request;
275 // wrap=virtual is not HTML4, but without it NS4 doesn't wrap
277 $readonly = $this->canEdit(); // || $this->isConcurrentUpdate();
279 return HTML::textarea(array('class' => 'wikiedit',
280 'name' => 'edit[content]',
281 'rows' => $request->getPref('editHeight'),
282 'cols' => $request->getPref('editWidth'),
283 'readonly' => (bool) $readonly,
284 'wrap' => 'virtual'),
288 function getFormElements () {
289 $request = &$this->request;
290 $page = &$this->page;
293 $h = array('action' => 'edit',
294 'pagename' => $page->getName(),
295 'version' => $this->version,
296 'edit[current_version]' => $this->_currentVersion);
298 $el['HIDDEN_INPUTS'] = HiddenInputs($h);
301 $el['EDIT_TEXTAREA'] = $this->getTextArea();
304 = HTML::input(array('type' => 'text',
305 'class' => 'wikitext',
306 'name' => 'edit[summary]',
309 'value' => $this->meta['summary']));
311 = HTML::input(array('type' => 'checkbox',
312 'name' => 'edit[minor_edit]',
313 'checked' => (bool) $this->meta['is_minor_edit']));
315 = HTML::input(array('type' => 'checkbox',
316 'name' => 'edit[markup]',
318 'checked' => $this->meta['markup'] < 2.0,
319 'id' => 'useOldMarkup',
320 'onclick' => 'showOldMarkupRules(this.checked)'));
323 = HTML::input(array('type' => 'checkbox',
324 'name' => 'edit[locked]',
325 'disabled' => (bool) !$this->user->isadmin(),
326 'checked' => (bool) $this->meta['locked']));
328 $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
331 //if (!$this->isConcurrentUpdate() && !$this->canEdit())
332 $el['SAVE_B'] = Button('submit:edit[save]', _("Save"), 'wikiaction');
334 $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
339 function _redirectToBrowsePage() {
340 $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
344 function _restoreState () {
345 $request = &$this->request;
347 $posted = $request->getArg('edit');
348 $request->setArg('edit', false);
350 if (!$posted || !$request->isPost()
351 || $request->getArg('action') != 'edit')
354 if (!isset($posted['content']) || !is_string($posted['content']))
356 $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
357 rtrim($posted['content']));
359 $this->_currentVersion = (int) $posted['current_version'];
361 if ($this->_currentVersion < 0)
363 if ($this->_currentVersion > $this->current->getVersion())
364 return false; // FIXME: some kind of warning?
366 $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
367 $meta['markup'] = $is_old_markup ? false : 2.0;
368 $meta['summary'] = trim(substr($posted['summary'], 0, 256));
369 $meta['locked'] = !empty($posted['locked']);
370 $meta['is_minor_edit'] = !empty($posted['minor_edit']);
372 $this->meta = array_merge($this->meta, $meta);
374 if (!empty($posted['preview']))
375 $this->editaction = 'preview';
376 elseif (!empty($posted['save']))
377 $this->editaction = 'save';
379 $this->editaction = 'edit';
384 function _initializeState () {
385 $request = &$this->request;
386 $current = &$this->current;
387 $selected = &$this->selected;
388 $user = &$this->user;
391 NoSuchRevision($request, $this->page, $this->version); // noreturn
393 $this->_currentVersion = $current->getVersion();
394 $this->_content = $selected->getPackedContent();
396 $this->meta['summary'] = '';
397 $this->meta['locked'] = $this->page->get('locked');
399 // If author same as previous author, default minor_edit to on.
400 $age = time() - $current->get('mtime');
401 $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
402 && $current->get('author') == $user->getId()
405 // Default for new pages is new-style markup.
406 if ($selected->hasDefaultContents())
407 $is_new_markup = true;
409 $is_new_markup = $selected->get('markup') >= 2.0;
411 $this->meta['markup'] = $is_new_markup ? 2.0: false;
412 $this->editaction = 'edit';
416 class LoadFileConflictPageEditor
419 function editPage ($saveFailed = true) {
422 if ($this->canEdit()) {
423 if ($this->isInitialEdit())
424 return $this->viewSource();
425 $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
427 elseif ($this->editaction == 'save') {
428 if ($this->savePage())
429 return true; // Page saved.
433 if ($saveFailed || $this->isConcurrentUpdate())
435 // Get the text of the original page, and the two conflicting edits
436 // The diff class takes arrays as input. So retrieve content as
437 // an array, or convert it as necesary.
438 $orig = $this->page->getRevision($this->_currentVersion);
439 $this_content = explode("\n", $this->_content);
440 $other_content = $this->current->getContent();
441 include_once("lib/diff.php");
442 $diff2 = new Diff($other_content, $this_content);
443 $context_lines = max(4, count($other_content) + 1,
444 count($this_content) + 1);
445 $fmt = new BlockDiffFormatter($context_lines);
447 $this->_content = $fmt->format($diff2);
448 // FIXME: integrate this into class BlockDiffFormatter
449 $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
451 $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
454 $this->_currentVersion = $this->current->getVersion();
455 $this->version = $this->_currentVersion;
456 $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
459 if ($this->editaction == 'preview')
460 $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
462 // FIXME: NOT_CURRENT_MESSAGE?
464 $tokens = array_merge($tokens, $this->getFormElements());
466 return $this->output('editpage', _("Merge and Edit: %s"), $tokens);
467 // FIXME: this doesn't display
470 function output ($template, $title_fs, $tokens) {
471 $selected = &$this->selected;
472 $current = &$this->current;
474 if ($selected && $selected->getVersion() != $current->getVersion()) {
476 $pagelink = WikiLink($selected);
480 $pagelink = WikiLink($this->page);
483 $title = new FormattedText ($title_fs, $pagelink);
484 $template = Template($template, $tokens);
486 //GeneratePage($template, $title, $rev);
490 function getConflictMessage () {
491 $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.",
494 HTML::p(_("Please check it through before saving."))));
500 $Log: not supported by cvs2svn $
501 Revision 1.52 2003/01/03 22:22:00 carstenklapp
502 Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
504 Revision 1.51 2003/01/03 02:43:26 carstenklapp
505 New class LoadFileConflictPageEditor, for merging / comparing a loaded
506 pgsrc file with an existing page.
514 // c-hanging-comment-ender-p: nil
515 // indent-tabs-mode: nil