]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
Adjustments to english instructions for conflicting edits.
[SourceForge/phpwiki.git] / lib / editpage.php
1 <?php
2 rcs_id('$Id: editpage.php,v 1.44 2002-02-23 05:53:14 carstenklapp Exp $');
3
4 require_once('lib/Template.php');
5
6 class PageEditor
7 {
8     function PageEditor (&$request) {
9         $this->request = &$request;
10
11         $this->user = $request->getUser();
12         $this->page = $request->getPage();
13
14         $this->current = $this->page->getCurrentRevision();
15
16         $this->meta = array('author' => $this->user->getId(),
17                             'locked' => $this->page->get('locked'),
18                             'author_id' => $this->user->getAuthenticatedId());
19
20         $version = $request->getArg('version');
21         if ($version !== false) {
22             $this->selected = $this->page->getRevision($version);
23             $this->version = $version;
24         }
25         else {
26             $this->selected = $this->current;
27             $this->version = $this->current->getVersion();
28         }
29
30         if ($this->_restoreState()) {
31             $this->_initialEdit = false;
32         }
33         else {
34             $this->_initializeState();
35             $this->_initialEdit = true;
36         }
37     }
38
39     function editPage () {
40         $saveFailed = false;
41         $tokens = array();
42
43         if ($this->canEdit()) {
44             if ($this->isInitialEdit())
45                 return $this->viewSource();
46             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
47         }
48         elseif ($this->editaction == 'save') {
49             if ($this->savePage())
50                 return true;    // Page saved.
51             $saveFailed = true;
52         }
53
54         if ($saveFailed || $this->isConcurrentUpdate())
55         {
56             // Get the text of the original page, and the two conflicting edits
57             // The diff3 class takes arrays as input.  So retrieve content as
58             // an array, or convert it as necesary.
59             $orig = $this->page->getRevision($this->_currentVersion);
60             $orig_content = $orig->getContent();
61             $this_content = explode("\n", $this->_content);
62             $other_content = $this->current->getContent();
63             include_once("lib/diff3.php");
64             $diff = new diff3($orig_content, $this_content, $other_content);
65             $output = $diff->merged_output(_("Your version"), _("Other version"));
66             // Set the content of the textarea to the merged diff output, and update the version
67             $this->_content = implode ("\n", $output);
68             $this->_currentVersion = $this->current->getVersion();
69             $this->version = $this->_currentVersion;
70             $unresolved = $diff->ConflictingBlocks;
71             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage($unresolved);
72         }
73
74         if ($this->editaction == 'preview')
75             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
76
77         // FIXME: NOT_CURRENT_MESSAGE?
78
79         $tokens = array_merge($tokens, $this->getFormElements());
80
81         return $this->output('editpage', _("Edit: %s"), $tokens);
82     }
83
84     function output ($template, $title_fs, $tokens) {
85         $selected = &$this->selected;
86         $current = &$this->current;
87
88         if ($selected && $selected->getVersion() != $current->getVersion()) {
89             $rev = $selected;
90             $pagelink = WikiLink($selected);
91         }
92         else {
93             $rev = $current;
94             $pagelink = WikiLink($this->page);
95         }
96
97
98         $title = new FormattedText ($title_fs, $pagelink);
99         $template = Template($template, $tokens);
100
101         GeneratePage($template, $title, $rev);
102         return true;
103     }
104
105
106     function viewSource ($tokens = false) {
107         assert($this->isInitialEdit());
108         assert($this->selected);
109
110         $tokens['PAGE_SOURCE'] = $this->_content;
111
112         return $this->output('viewsource', _("View Source: %s"), $tokens);
113     }
114
115     function setPageLockChanged($isadmin, $lock, &$page) {
116         if ($isadmin) {
117             if (! $page->get('locked') == $lock) {
118                 $request = &$this->request;
119                 $request->setArg('lockchanged', true); //is it safe to add new args to $request like this?
120             }
121             $page->set('locked', $lock);
122         }
123     }
124
125     function savePage () {
126         $request = &$this->request;
127
128         if ($this->isUnchanged()) {
129             // Allow admin lock/unlock even if
130             // no text changes were made.
131             if ($isadmin = $this->user->isadmin()) {
132                 $page = &$this->page;
133                 $lock = $this->meta['locked'];
134                 $this->setPageLockChanged($isadmin, $lock, &$page);
135             }
136             // Save failed. No changes made.
137             include_once('lib/display.php');
138             // force browse of current version:
139             $request->setArg('version', false);
140             displayPage($request, 'nochanges');
141             return true;
142         }
143
144         $page = &$this->page;
145         $lock = $this->meta['locked'];
146         $this->meta['locked'] = ''; // hackish
147
148         // Save new revision
149         $newrevision = $page->createRevision($this->_currentVersion + 1,
150                                              $this->_content,
151                                              $this->meta,
152                                              ExtractWikiPageLinks($this->_content));
153         if (!is_object($newrevision)) {
154             // Save failed.  (Concurrent updates).
155             return false;
156         }
157         // New contents successfully saved...
158         if ($isadmin = $this->user->isadmin())
159             $this->setPageLockChanged($isadmin, $lock, &$page);
160
161         // Clean out archived versions of this page.
162         include_once('lib/ArchiveCleaner.php');
163         $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
164         $cleaner->cleanPageRevisions($page);
165
166         $dbi = $request->getDbh();
167         $warnings = $dbi->GenericWarnings();
168
169         global $Theme;
170         if (empty($warnings) && ! $Theme->getImageURL('signature')) {
171             // Do redirect to browse page if no signature has
172             // been defined.  In this case, the user will most
173             // likely not see the rest of the HTML we generate
174             // (below).
175             $request->redirect(WikiURL($page, false, 'absolute_url'));
176         }
177
178         // Force browse of current page version.
179         $request->setArg('version', false);
180
181         require_once('lib/PageType.php');
182         $transformedText = PageType($newrevision);
183         $template = Template('savepage',
184                              array('CONTENT' => $transformedText));
185         //include_once('lib/BlockParser.php');
186         //$template = Template('savepage',
187         //                     array('CONTENT' => TransformText($newrevision)));
188         if (!empty($warnings))
189             $template->replace('WARNINGS', $warnings);
190
191         $pagelink = WikiLink($page);
192
193         GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
194         return true;
195     }
196
197     function isConcurrentUpdate () {
198         assert($this->current->getVersion() >= $this->_currentVersion);
199         return $this->current->getVersion() != $this->_currentVersion;
200     }
201
202     function canEdit () {
203         return $this->page->get('locked') && !$this->user->isAdmin();
204     }
205
206     function isInitialEdit () {
207         return $this->_initialEdit;
208     }
209
210     function isUnchanged () {
211         $current = &$this->current;
212
213         if ($this->meta['markup'] !=  $current->get('markup'))
214             return false;
215
216         return $this->_content == $current->getPackedContent();
217     }
218
219     function getPreview () {
220         require_once('lib/PageType.php');
221         return PageType($this->_content, $this->page->getName(), $this->meta['markup']);
222
223         //include_once('lib/BlockParser.php');
224         //return TransformText($this->_content, $this->meta['markup']);
225     }
226
227     function getLockedMessage () {
228         return
229         HTML(HTML::h2(_("Page Locked")),
230              HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
231              HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
232              HTML::p(_("Sorry for the inconvenience.")));
233     }
234
235     function getConflictMessage ($unresolved = false) {
236         /*
237          xgettext only knows about c/c++ line-continuation strings
238          it does not know about php's dot operator.
239          We want to translate this entire paragraph as one string, of course.
240          */
241
242         //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
243
244         if ($unresolved)
245             $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."),
246                                 "<<<<<<< ". _("Your version"),
247                                 ">>>>>>> . _("Other version"));
248         else
249             $message = HTML::p(_("Please check it through before saving."));
250
251
252
253         /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
254           HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
255                        $re_edit_link)),
256           HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
257           HTML::li(_("Save your updated changes.")));
258         */
259         return HTML(HTML::h2(_("Conflicting Edits!")),
260                     HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
261                     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.")),
262                     $message);
263     }
264
265
266     function getTextArea () {
267         $request = &$this->request;
268
269         // wrap=virtual is not HTML4, but without it NS4 doesn't wrap long lines
270         $readonly = $this->canEdit(); // || $this->isConcurrentUpdate();
271
272         return HTML::textarea(array('class'    => 'wikiedit',
273                                     'name'     => 'edit[content]',
274                                     'rows'     => $request->getPref('editHeight'),
275                                     'cols'     => $request->getPref('editWidth'),
276                                     'readonly' => (bool) $readonly,
277                                     'wrap'     => 'virtual'),
278                               $this->_content);
279     }
280
281     function getFormElements () {
282         $request = &$this->request;
283         $page = &$this->page;
284
285
286         $h = array('action'   => 'edit',
287                    'pagename' => $page->getName(),
288                    'version'  => $this->version,
289                    'edit[current_version]' => $this->_currentVersion);
290
291         $el['HIDDEN_INPUTS'] = HiddenInputs($h);
292
293
294         $el['EDIT_TEXTAREA'] = $this->getTextArea();
295
296         $el['SUMMARY_INPUT']
297             = HTML::input(array('type'  => 'text',
298                                 'class' => 'wikitext',
299                                 'name'  => 'edit[summary]',
300                                 'size'  => 50,
301                                 'maxlength' => 256,
302                                 'value' => $this->meta['summary']));
303         $el['MINOR_EDIT_CB']
304             = HTML::input(array('type' => 'checkbox',
305                                 'name'  => 'edit[minor_edit]',
306                                 'checked' => (bool) $this->meta['is_minor_edit']));
307         $el['NEW_MARKUP_CB']
308             = HTML::input(array('type' => 'checkbox',
309                                 'name' => 'edit[markup]',
310                                 'value' => 'new',
311                                 'checked' => $this->meta['markup'] >= 2.0));
312
313         $el['LOCKED_CB']
314             = HTML::input(array('type' => 'checkbox',
315                                 'name' => 'edit[locked]',
316                                 'disabled' => (bool) !$this->user->isadmin(),
317                                 'checked'  => (bool) $this->meta['locked']));
318
319         $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"), 'wikiaction');
320
321         //if (!$this->isConcurrentUpdate() && !$this->canEdit())
322         $el['SAVE_B'] = Button('submit:edit[save]', _("Save"), 'wikiaction');
323
324         $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
325
326         return $el;
327     }
328
329
330     function _restoreState () {
331         $request = &$this->request;
332
333         $posted = $request->getArg('edit');
334         $request->setArg('edit', false);
335
336         if (!$posted || !$request->isPost() || $request->getArg('action') != 'edit')
337             return false;
338
339         if (!isset($posted['content']) || !is_string($posted['content']))
340             return false;
341         $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
342                                         rtrim($posted['content']));
343
344         $this->_currentVersion = (int) $posted['current_version'];
345
346         if ($this->_currentVersion < 0)
347             return false;
348         if ($this->_currentVersion > $this->current->getVersion())
349             return false;       // FIXME: some kind of warning?
350
351         $is_new_markup = !empty($posted['markup']) && $posted['markup'] == 'new';
352         $meta['markup'] = $is_new_markup ? 2.0: false;
353         $meta['summary'] = trim(substr($posted['summary'], 0, 256));
354         $meta['locked'] = !empty($posted['locked']);
355         $meta['is_minor_edit'] = !empty($posted['minor_edit']);
356
357         $this->meta = array_merge($this->meta, $meta);
358
359         if (!empty($posted['preview']))
360             $this->editaction = 'preview';
361         elseif (!empty($posted['save']))
362             $this->editaction = 'save';
363         else
364             $this->editaction = 'edit';
365
366         return true;
367     }
368
369     function _initializeState () {
370         $request = &$this->request;
371         $current = &$this->current;
372         $selected = &$this->selected;
373         $user = &$this->user;
374
375         if (!$selected)
376             NoSuchRevision($request, $this->page, $this->version); // noreturn
377
378         $this->_currentVersion = $current->getVersion();
379         $this->_content = $selected->getPackedContent();
380
381         $this->meta['summary'] = '';
382         $this->meta['locked'] = $this->page->get('locked');
383
384         // If author same as previous author, default minor_edit to on.
385         $age = time() - $current->get('mtime');
386         $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
387                                          && $current->get('author') == $user->getId()
388                                          );
389
390         // Default for new pages is new-style markup.
391         if ($selected->hasDefaultContents())
392             $is_new_markup = true;
393         else
394             $is_new_markup = $selected->get('markup') >= 2.0;
395
396         $this->meta['markup'] = $is_new_markup ? 2.0: false;
397         $this->editaction = 'edit';
398     }
399 }
400
401 // Local Variables:
402 // mode: php
403 // tab-width: 8
404 // c-basic-offset: 4
405 // c-hanging-comment-ender-p: nil
406 // indent-tabs-mode: nil
407 // End:
408 ?>