]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
just some comments
[SourceForge/phpwiki.git] / lib / editpage.php
1 <?php
2 rcs_id('$Id: editpage.php,v 1.75 2004-09-16 08:00:52 rurban Exp $');
3
4 require_once('lib/Template.php');
5
6 // USE_HTMLAREA - Support for some WYSIWYG HTML Editor
7 // Not yet enabled, since we cannot convert HTML to Wiki Markup yet.
8 // (See HtmlParser.php for the ongoing efforts)
9 // We might use a HTML PageType, which is contra wiki, but some people might prefer HTML markup.
10 // TODO: Change from constant to user preference variable (checkbox setting),
11 //       when HtmlParser is finished.
12 if (!defined('USE_HTMLAREA')) define('USE_HTMLAREA',false);
13 if (USE_HTMLAREA) require_once('lib/htmlarea.php');
14
15 class PageEditor
16 {
17     function PageEditor (&$request) {
18         $this->request = &$request;
19
20         $this->user = $request->getUser();
21         $this->page = $request->getPage();
22
23         $this->current = $this->page->getCurrentRevision(false);
24
25         // HACKish short circuit to browse on action=create
26         if ($request->getArg('action') == 'create') {
27             if (! $this->current->hasDefaultContents()) 
28                 $request->redirect(WikiURL($this->page->getName())); // noreturn
29         }
30         
31         
32         $this->meta = array('author' => $this->user->getId(),
33                             'author_id' => $this->user->getAuthenticatedId(),
34                             'mtime' => time());
35         
36         $this->tokens = array();
37         
38         $version = $request->getArg('version');
39         if ($version !== false) {
40             $this->selected = $this->page->getRevision($version);
41             $this->version = $version;
42         }
43         else {
44             $this->version = $this->current->getVersion();
45             $this->selected = $this->page->getRevision($this->version);
46         }
47
48         if ($this->_restoreState()) {
49             $this->_initialEdit = false;
50         }
51         else {
52             $this->_initializeState();
53             $this->_initialEdit = true;
54
55             // The edit request has specified some initial content from a template 
56             if (  ($template = $request->getArg('template')) and 
57                   $request->_dbi->isWikiPage($template)) {
58                 $page = $request->_dbi->getPage($template);
59                 $current = $page->getCurrentRevision();
60                 $this->_content = $current->getPackedContent();
61             } elseif ($initial_content = $request->getArg('initial_content')) {
62                 $this->_content = $initial_content;
63                 $this->_redirect_to = $request->getArg('save_and_redirect_to');
64             }
65         }
66         if (!headers_sent())
67             header("Content-Type: text/html; charset=" . $GLOBALS['charset']);
68     }
69
70     function editPage () {
71         global $WikiTheme;
72         $saveFailed = false;
73         $tokens = &$this->tokens;
74
75         if (! $this->canEdit()) {
76             if ($this->isInitialEdit())
77                 return $this->viewSource();
78             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
79         }
80         elseif ($this->request->getArg('save_and_redirect_to') != "") {
81             if ($this->savePage()) {
82                 // noreturn
83                 $this->request->redirect(WikiURL($this->request->getArg('save_and_redirect_to')));
84                 return true;    // Page saved.
85             }
86             $saveFailed = true;
87         }
88         elseif ($this->editaction == 'save') {
89             if ($this->savePage()) {
90                 return true;    // Page saved.
91             }
92             $saveFailed = true;
93         }
94
95         if ($saveFailed || $this->isConcurrentUpdate())
96         {
97             // Get the text of the original page, and the two conflicting edits
98             // The diff3 class takes arrays as input.  So retrieve content as
99             // an array, or convert it as necesary.
100             $orig = $this->page->getRevision($this->_currentVersion);
101             // FIXME: what if _currentVersion has be deleted?
102             $orig_content = $orig->getContent();
103             $this_content = explode("\n", $this->_content);
104             $other_content = $this->current->getContent();
105             include_once("lib/diff3.php");
106             $diff = new diff3($orig_content, $this_content, $other_content);
107             $output = $diff->merged_output(_("Your version"), _("Other version"));
108             // Set the content of the textarea to the merged diff
109             // output, and update the version
110             $this->_content = implode ("\n", $output);
111             $this->_currentVersion = $this->current->getVersion();
112             $this->version = $this->_currentVersion;
113             $unresolved = $diff->ConflictingBlocks;
114             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage($unresolved);
115         }
116
117         if ($this->editaction == 'preview')
118             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
119
120         // FIXME: NOT_CURRENT_MESSAGE?
121         $tokens = array_merge($tokens, $this->getFormElements());
122
123         //FIXME: enable Undo button for all other buttons also, not only the search/replace button
124         if (defined('JS_SEARCHREPLACE') and JS_SEARCHREPLACE) {
125             $tokens['JS_SEARCHREPLACE'] = 1;
126             $undo_btn = $WikiTheme->getImageURL("ed_undo.gif"); 
127             $undo_d_btn = $WikiTheme->getImageURL("ed_undo_d.gif"); 
128             // JS_SEARCHREPLACE from walterzorn.de
129             $WikiTheme->addMoreHeaders(Javascript("
130 var f, sr_undo, replacewin, undo_buffer=new Array(), undo_buffer_index=0;
131
132 function define_f() {
133    f=document.getElementById('editpage');
134    f.editarea=document.getElementById('edit[content]');
135    sr_undo=document.getElementById('sr_undo');
136    undo_enable(false);
137    f.editarea.focus();
138 }
139 function undo_enable(bool) {
140    if (bool) {
141      sr_undo.src='".$undo_btn."';
142      sr_undo.alt='"
143 ._("Undo")
144 ."';
145      sr_undo.disabled = false;
146    } else {
147      sr_undo.src='".$undo_d_btn."';
148      sr_undo.alt='"
149 ._("Undo disabled")
150 ."';
151      sr_undo.disabled = true;
152      if(sr_undo.blur) sr_undo.blur();
153   }
154 }
155
156 function replace() {
157    replacewin=window.open('','','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,height=90,width=450');
158    replacewin.window.document.write('<html><head><title>"
159 ._("Search & Replace")
160 ."</title><style type=\"text/css\"><'+'!'+'-- body, input {font-family:Tahoma,Arial,Helvetica,sans-serif;font-size:10pt;font-weight:bold;} td {font-size:9pt}  --'+'></style></head><body bgcolor=\"#dddddd\" onload=\"if(document.forms[0].ein.focus) document.forms[0].ein.focus()\"><form><center><table><tr><td align=\"right\">'+'"
161 ._("Search")
162 .":</td><td align=\"left\"><input type=\"text\" name=\"ein\" size=\"45\" maxlength=\"500\"></td></tr><tr><td align=\"right\">'+' "
163 ._("Replace with")
164 .":</td><td align=\"left\"><input type=\"text\" name=\"aus\" size=\"45\" maxlength=\"500\"></td></tr><tr><td colspan=\"2\" align=\"center\"><input type=\"button\" value=\" "
165 ._("OK")
166 ." \" onclick=\"self.opener.do_replace()\">&nbsp;&nbsp;&nbsp;<input type=\"button\" value=\""
167 ._("Close")
168 ."\" onclick=\"self.close()\"></td></tr></table></center></form></body></html>');
169    replacewin.window.document.close();
170 }
171
172 function do_replace() {
173    var txt=undo_buffer[undo_buffer_index]=f.editarea.value, ein=new RegExp(replacewin.document.forms[0].ein.value,'g'), aus=replacewin.document.forms[0].aus.value;
174    if(ein==''||ein==null) {
175       replacewin.window.document.forms[0].ein.focus();
176       return;
177    }
178    var z_repl=txt.match(ein)? txt.match(ein).length : 0;
179    txt=txt.replace(ein,aus);
180    ein=ein.toString().substring(1,ein.toString().length-2);
181    result(z_repl, 'Substring \"'+ein+'\" found '+z_repl+' times. Replace with \"'+aus+'\"?', txt, 'String \"'+ein+'\" not found.');
182    replacewin.window.focus();
183    replacewin.window.document.forms[0].ein.focus();
184 }
185 function result(zahl,frage,txt,alert_txt) {
186    if(zahl>0) {
187       if(window.confirm(frage)==true) {
188          f.editarea.value=txt;
189          undo_buffer_index++;
190          undo_enable(true);
191       }
192    } else alert(alert_txt);
193 }
194 function do_undo() {
195    if(undo_buffer_index==0) return;
196    else if(undo_buffer_index>0) {
197       f.editarea.value=undo_buffer[undo_buffer_index-1];
198       undo_buffer[undo_buffer_index]=null;
199       undo_buffer_index--;
200       if(undo_buffer_index==0) {
201          alert('Operation undone.');
202          undo_enable(false);
203       }
204    }
205 }
206 //save a snapshot in the undo buffer (unused)
207 function speich() {
208    undo_buffer[undo_buffer_index]=f.editarea.value;
209    undo_buffer_index++;
210    undo_enable(true);
211 }
212 "));
213             $WikiTheme->addMoreAttr('body'," onload='define_f()'");
214         } else {
215             $WikiTheme->addMoreAttr('body',"document.getElementById('edit[content]').editarea.focus()");
216         }
217         if (defined('ENABLE_EDIT_TOOLBAR') and ENABLE_EDIT_TOOLBAR) {
218             $WikiTheme->addMoreHeaders(JavaScript('',array('src' => $WikiTheme->_findData("toolbar.js"))));
219             $tokens['EDIT_TOOLBAR'] = $this->toolbar();
220         } else {
221             $tokens['EDIT_TOOLBAR'] = '';
222         }
223
224         return $this->output('editpage', _("Edit: %s"));
225     }
226
227     function toolbar () {
228         global $WikiTheme;
229         $toolarray = array(
230                            array(
231                                  "image"=>"ed_format_bold.gif",
232                                  "open"=>"*",
233                                  "close"=>"*",
234                                  "sample"=>_("Bold text"),
235                                  "tip"=>_("Bold text")),
236                            array("image"=>"ed_format_italic.gif",
237                                  "open"=>"_",
238                                  "close"=>"_",
239                                  "sample"=>_("Italic text"),
240                                  "tip"=>_("Italic text")),
241                            array("image"=>"ed_pagelink.gif",
242                                  "open"=>"[",
243                                  "close"=>"]",
244                                  "sample"=>_("optional label | PageName"),
245                                  "tip"=>_("Link to page")),
246                            array("image"=>"ed_link.gif",
247                                  "open"=>"[",
248                                  "close"=>"]",
249                                  "sample"=>_("optional label | http://www.example.com"),
250                                  "tip"=>_("External link (remember http:// prefix)")),
251                            array("image"=>"ed_headline.gif",
252                                  "open"=>"\\n!!! ",
253                                  "close"=>"\\n",
254                                  "sample"=>_("Headline text"),
255                                  "tip"=>_("Level 1 headline")),
256                            array("image"=>"ed_image.gif",
257                                  "open"=>"[ ",
258                                  "close"=>" ]",
259                                  "sample"=>_("Example.jpg"),
260                                  "tip"=>_("Embedded image")),
261                            array("image"=>"ed_nowiki.gif",
262                                  "open"=>"\\n\\<verbatim\\>\\n",
263                                  "close"=>"\\n\\</verbatim\\>\\n",
264                                  "sample"=>_("Insert non-formatted text here"),
265                                  "tip"=>_("Ignore wiki formatting")),
266                            array("image"=>"ed_sig.gif",
267                                  "open" => " --" . $GLOBALS['request']->_user->UserName(),
268                                  "close" => "",
269                                  "sample"=>"",
270                                  "tip"=>_("Your signature")),
271                            array("image"=>"ed_hr.gif",
272                                  "open"=>"\\n----\\n",
273                                  "close"=>"",
274                                  "sample"=>"",
275                                  "tip"=>_("Horizontal line"))
276                            );
277         $toolbar = "document.writeln(\"<div class=\\\"edit-toolbar\\\" id=\\\"toolbar\\\">\");\n";
278
279         $btn = new SubmitImageButton(_("Save"), "edit[save]", 'toolbar', $WikiTheme->getImageURL("ed_save.gif"));
280         $btn->addTooltip(_("Save"));
281         $toolbar.='document.writeln("'.addslashes($btn->asXml()).'");'."\n";
282         $btn = new SubmitImageButton(_("Preview"), "edit[preview]", 'toolbar', $WikiTheme->getImageURL("ed_preview.gif"));
283         $btn->addTooltip(_("Preview"));
284         $toolbar.='document.writeln("'.addslashes($btn->asXml()).'");'."\n";
285
286         foreach ($toolarray as $tool) {
287             $image = $WikiTheme->getImageURL($tool["image"]);
288             $open  = $tool["open"];
289             $close = $tool["close"];
290             $sample = addslashes( $tool["sample"] );
291             // Note that we use the tip both for the ALT tag and the TITLE tag of the image.
292             // Older browsers show a "speedtip" type message only for ALT.
293             // Ideally these should be different, realistically they
294             // probably don't need to be.
295             $tip = addslashes( $tool["tip"] );
296             $toolbar.="addButton('$image','$tip','$open','$close','$sample');\n";
297         }
298         $toolbar.="addInfobox('" . addslashes( _("Click a button to get an example text") ) . "');\n";
299         if (defined('JS_SEARCHREPLACE') and JS_SEARCHREPLACE) {
300             $undo_d_btn = $WikiTheme->getImageURL("ed_undo_d.gif"); 
301             //$redo_btn = $WikiTheme->getImageURL("ed_redo.gif");
302             $sr_btn   = $WikiTheme->getImageURL("ed_replace.gif");
303             $sr_js = '<input type="image" class="toolbar" id="sr_undo" src="'.$undo_d_btn.'" title="'._("Undo Search & Replace").'" disabled="disabled" value="Undo" onfocus="if(this.blur && undo_buffer_index==0) this.blur()" onclick="do_undo()">'
304                 // . '<input type="image" class="toolbar" src="'.$redo_btn.'" title="'._("Snap").'" onclick="speich()">'
305                 . '<input type="image" class="toolbar" src="'.$sr_btn.'" title="'._("Search & Replace").'" onclick="replace()">';
306             $toolbar.='document.writeln("'.addslashes($sr_js).'");'."\n";
307         }
308         // More:
309         // Button to generate pagenames, display in extra window as pulldown and insert
310         // Button to generate plugins, display in extra window as pulldown and insert
311         // Button to generate categories, display in extra window as pulldown and insert
312         $toolbar.="document.writeln(\"</div>\");";
313         return Javascript($toolbar);
314     }
315
316     function output ($template, $title_fs) {
317         global $WikiTheme;
318         $selected = &$this->selected;
319         $current = &$this->current;
320
321         if ($selected && $selected->getVersion() != $current->getVersion()) {
322             $rev = $selected;
323             $pagelink = WikiLink($selected);
324         }
325         else {
326             $rev = $current;
327             $pagelink = WikiLink($this->page);
328         }
329
330
331         $title = new FormattedText ($title_fs, $pagelink);
332         if ($template == 'editpage' and USE_HTMLAREA) {
333             $WikiTheme->addMoreHeaders(Edit_HtmlArea_Head());
334             //$tokens['PAGE_SOURCE'] = Edit_HtmlArea_ConvertBefore($this->_content);
335         }
336         $template = Template($template, $this->tokens);
337         GeneratePage($template, $title, $rev);
338         return true;
339     }
340
341
342     function viewSource () {
343         assert($this->isInitialEdit());
344         assert($this->selected);
345
346         $this->tokens['PAGE_SOURCE'] = $this->_content;
347         return $this->output('viewsource', _("View Source: %s"));
348     }
349
350     function updateLock() {
351         if ((bool)$this->page->get('locked') == (bool)$this->locked)
352             return false;       // Not changed.
353
354         if (!$this->user->isAdmin()) {
355             // FIXME: some sort of message
356             return false;         // not allowed.
357         }
358
359         $this->page->set('locked', (bool)$this->locked);
360         $this->tokens['LOCK_CHANGED_MSG']
361             = $this->locked ? _("Page now locked.") : _("Page now unlocked.");
362
363         return true;            // lock changed.
364     }
365
366     function savePage () {
367         $request = &$this->request;
368
369         if ($this->isUnchanged()) {
370             // Allow admin lock/unlock even if
371             // no text changes were made.
372             if ($this->updateLock()) {
373                 $dbi = $request->getDbh();
374                 $dbi->touch();
375             }
376             // Save failed. No changes made.
377             $this->_redirectToBrowsePage();
378             // user will probably not see the rest of this...
379             include_once('lib/display.php');
380             // force browse of current version:
381             $request->setArg('version', false);
382             displayPage($request, 'nochanges');
383             return true;
384         }
385
386         $page = &$this->page;
387
388         // Include any meta-data from original page version which
389         // has not been explicitly updated.
390         // (Except don't propagate pgsrc_version --- moot for now,
391         //  because at present it never gets into the db...)
392         $meta = $this->selected->getMetaData();
393         unset($meta['pgsrc_version']);
394         $meta = array_merge($meta, $this->meta);
395         
396         // Save new revision
397         $this->_content = $this->getContent();
398         $newrevision = $page->save($this->_content, $this->_currentVersion + 1, $meta);
399         if (!isa($newrevision, 'wikidb_pagerevision')) {
400             // Save failed.  (Concurrent updates).
401             return false;
402         }
403         
404         // New contents successfully saved...
405         $this->updateLock();
406
407         // Clean out archived versions of this page.
408         include_once('lib/ArchiveCleaner.php');
409         $cleaner = new ArchiveCleaner($GLOBALS['ExpireParams']);
410         $cleaner->cleanPageRevisions($page);
411
412         /* generate notification emails done in WikiDB::save to catch all direct calls 
413           (admin plugins) */
414
415         $dbi = $request->getDbh();
416         $warnings = $dbi->GenericWarnings();
417         $dbi->touch();
418         
419         global $WikiTheme;
420         if (empty($warnings) && ! $WikiTheme->getImageURL('signature')) {
421             // Do redirect to browse page if no signature has
422             // been defined.  In this case, the user will most
423             // likely not see the rest of the HTML we generate
424             // (below).
425             $this->_redirectToBrowsePage();
426         }
427
428         // Force browse of current page version.
429         $request->setArg('version', false);
430         //$request->setArg('action', false);
431
432         $template = Template('savepage', $this->tokens);
433         $template->replace('CONTENT', $newrevision->getTransformedContent());
434         if (!empty($warnings))
435             $template->replace('WARNINGS', $warnings);
436
437         $pagelink = WikiLink($page);
438
439         GeneratePage($template, fmt("Saved: %s", $pagelink), $newrevision);
440         return true;
441     }
442
443     function isConcurrentUpdate () {
444         assert($this->current->getVersion() >= $this->_currentVersion);
445         return $this->current->getVersion() != $this->_currentVersion;
446     }
447
448     function canEdit () {
449         return !$this->page->get('locked') || $this->user->isAdmin();
450     }
451
452     function isInitialEdit () {
453         return $this->_initialEdit;
454     }
455
456     function isUnchanged () {
457         $current = &$this->current;
458
459         if ($this->meta['markup'] !=  $current->get('markup'))
460             return false;
461
462         return $this->_content == $current->getPackedContent();
463     }
464
465     function getPreview () {
466         include_once('lib/PageType.php');
467         $this->_content = $this->getContent();
468         return new TransformedText($this->page, $this->_content, $this->meta);
469     }
470
471     // possibly convert HTMLAREA content back to Wiki markup
472     function getContent () {
473         if (USE_HTMLAREA) {
474             $xml_output = Edit_HtmlArea_ConvertAfter($this->_content);
475             $this->_content = join("",$xml_output->_content);
476             return $this->_content;
477         } else {
478             return $this->_content;
479         }
480     }
481
482     function getLockedMessage () {
483         return
484             HTML(HTML::h2(_("Page Locked")),
485                  HTML::p(_("This page has been locked by the administrator so your changes can not be saved.")),
486                  HTML::p(_("(Copy your changes to the clipboard. You can try editing a different page or save your text in a text editor.)")),
487                  HTML::p(_("Sorry for the inconvenience.")));
488     }
489
490     function getConflictMessage ($unresolved = false) {
491         /*
492          xgettext only knows about c/c++ line-continuation strings
493          it does not know about php's dot operator.
494          We want to translate this entire paragraph as one string, of course.
495          */
496
497         //$re_edit_link = Button('edit', _("Edit the new version"), $this->page);
498
499         if ($unresolved)
500             $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.",
501                                 "<<<<<<< ". _("Your version"),
502                                 ">>>>>>> ". _("Other version")));
503         else
504             $message = HTML::p(_("Please check it through before saving."));
505
506
507
508         /*$steps = HTML::ol(HTML::li(_("Copy your changes to the clipboard or to another temporary place (e.g. text editor).")),
509           HTML::li(fmt("%s of the page. You should now see the most current version of the page. Your changes are no longer there.",
510                        $re_edit_link)),
511           HTML::li(_("Make changes to the file again. Paste your additions from the clipboard (or text editor).")),
512           HTML::li(_("Save your updated changes.")));
513         */
514         return
515             HTML(HTML::h2(_("Conflicting Edits!")),
516                  HTML::p(_("In the time since you started editing this page, another user has saved a new version of it.")),
517                  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.")),
518                  $message);
519     }
520
521
522     function getTextArea () {
523         $request = &$this->request;
524
525         // wrap=virtual is not HTML4, but without it NS4 doesn't wrap
526         // long lines
527         $readonly = ! $this->canEdit(); // || $this->isConcurrentUpdate();
528         if (USE_HTMLAREA) {
529             $html = $this->getPreview();
530             $this->_wikicontent = $this->_content;
531             $this->_content = $html->asXML();
532         }
533
534         /** <textarea wrap="virtual"> is not valid xhtml but Netscape 4 requires it
535          * to wrap long lines.
536          */
537         $textarea = HTML::textarea(array('class' => 'wikiedit',
538                                          'name' => 'edit[content]',
539                                          'id'   => 'edit[content]',
540                                          'rows' => $request->getPref('editHeight'),
541                                          'cols' => $request->getPref('editWidth'),
542                                          'readonly' => (bool) $readonly),
543                                    $this->_content);
544         if (isBrowserNS4())
545             $textarea->setAttr('wrap','virtual');
546         if (USE_HTMLAREA)
547             return Edit_HtmlArea_Textarea($textarea,$this->_wikicontent,'edit[content]');
548         else
549             return $textarea;
550     }
551
552     function getFormElements () {
553         $request = &$this->request;
554         $page = &$this->page;
555
556
557         $h = array('action'   => 'edit',
558                    'pagename' => $page->getName(),
559                    'version'  => $this->version,
560                    'edit[pagetype]' => $this->meta['pagetype'],
561                    'edit[current_version]' => $this->_currentVersion);
562
563         $el['HIDDEN_INPUTS'] = HiddenInputs($h);
564         $el['EDIT_TEXTAREA'] = $this->getTextArea();
565         $el['SUMMARY_INPUT']
566             = HTML::input(array('type'  => 'text',
567                                 'class' => 'wikitext',
568                                 'name'  => 'edit[summary]',
569                                 'size'  => 50,
570                                 'maxlength' => 256,
571                                 'value' => $this->meta['summary']));
572         $el['MINOR_EDIT_CB']
573             = HTML::input(array('type' => 'checkbox',
574                                 'name'  => 'edit[minor_edit]',
575                                 'checked' => (bool) $this->meta['is_minor_edit']));
576         $el['OLD_MARKUP_CB']
577             = HTML::input(array('type' => 'checkbox',
578                                 'name' => 'edit[markup]',
579                                 'value' => 'old',
580                                 'checked' => $this->meta['markup'] < 2.0,
581                                 'id' => 'useOldMarkup',
582                                 'onclick' => 'showOldMarkupRules(this.checked)'));
583
584         $el['LOCKED_CB']
585             = HTML::input(array('type' => 'checkbox',
586                                 'name' => 'edit[locked]',
587                                 'disabled' => (bool) !$this->user->isadmin(),
588                                 'checked'  => (bool) $this->locked));
589
590         $el['PREVIEW_B'] = Button('submit:edit[preview]', _("Preview"),
591                                   'wikiaction');
592
593         //if (!$this->isConcurrentUpdate() && $this->canEdit())
594         $el['SAVE_B'] = Button('submit:edit[save]', _("Save"), 'wikiaction');
595
596         $el['IS_CURRENT'] = $this->version == $this->current->getVersion();
597
598         return $el;
599     }
600
601     function _redirectToBrowsePage() {
602         $this->request->redirect(WikiURL($this->page, false, 'absolute_url'));
603     }
604     
605
606     function _restoreState () {
607         $request = &$this->request;
608
609         $posted = $request->getArg('edit');
610         $request->setArg('edit', false);
611
612         if (!$posted || !$request->isPost()
613             || $request->getArg('action') != 'edit')
614             return false;
615
616         if (!isset($posted['content']) || !is_string($posted['content']))
617             return false;
618         $this->_content = preg_replace('/[ \t\r]+\n/', "\n",
619                                         rtrim($posted['content']));
620         $this->_content = $this->getContent();
621
622         $this->_currentVersion = (int) $posted['current_version'];
623
624         if ($this->_currentVersion < 0)
625             return false;
626         if ($this->_currentVersion > $this->current->getVersion())
627             return false;       // FIXME: some kind of warning?
628
629         $is_old_markup = !empty($posted['markup']) && $posted['markup'] == 'old';
630         $meta['markup'] = $is_old_markup ? false : 2.0;
631         $meta['summary'] = trim(substr($posted['summary'], 0, 256));
632         $meta['is_minor_edit'] = !empty($posted['minor_edit']);
633         $meta['pagetype'] = !empty($posted['pagetype']) ? $posted['pagetype'] : false;
634         $this->meta = array_merge($this->meta, $meta);
635         $this->locked = !empty($posted['locked']);
636
637         if (!empty($posted['preview']))
638             $this->editaction = 'preview';
639         elseif (!empty($posted['save']))
640             $this->editaction = 'save';
641         else
642             $this->editaction = 'edit';
643
644         return true;
645     }
646
647     function _initializeState () {
648         $request = &$this->request;
649         $current = &$this->current;
650         $selected = &$this->selected;
651         $user = &$this->user;
652
653         if (!$selected)
654             NoSuchRevision($request, $this->page, $this->version); // noreturn
655
656         $this->_currentVersion = $current->getVersion();
657         $this->_content = $selected->getPackedContent();
658
659         $this->meta['summary'] = '';
660         $this->locked = $this->page->get('locked');
661
662         // If author same as previous author, default minor_edit to on.
663         $age = $this->meta['mtime'] - $current->get('mtime');
664         $this->meta['is_minor_edit'] = ( $age < MINOR_EDIT_TIMEOUT
665                                          && $current->get('author') == $user->getId()
666                                          );
667
668         // Default for new pages is new-style markup.
669         if ($selected->hasDefaultContents())
670             $is_new_markup = true;
671         else
672             $is_new_markup = $selected->get('markup') >= 2.0;
673
674         $this->meta['markup'] = $is_new_markup ? 2.0: false;
675         $this->meta['pagetype'] = $selected->get('pagetype');
676         $this->editaction = 'edit';
677     }
678 }
679
680 class LoadFileConflictPageEditor
681 extends PageEditor
682 {
683     function editPage ($saveFailed = true) {
684         $tokens = &$this->tokens;
685
686         if (!$this->canEdit()) {
687             if ($this->isInitialEdit())
688                 return $this->viewSource();
689             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
690         }
691         elseif ($this->editaction == 'save') {
692             if ($this->savePage())
693                 return true;    // Page saved.
694             $saveFailed = true;
695         }
696
697         if ($saveFailed || $this->isConcurrentUpdate())
698         {
699             // Get the text of the original page, and the two conflicting edits
700             // The diff class takes arrays as input.  So retrieve content as
701             // an array, or convert it as necesary.
702             $orig = $this->page->getRevision($this->_currentVersion);
703             $this_content = explode("\n", $this->_content);
704             $other_content = $this->current->getContent();
705             include_once("lib/diff.php");
706             $diff2 = new Diff($other_content, $this_content);
707             $context_lines = max(4, count($other_content) + 1,
708                                  count($this_content) + 1);
709             $fmt = new BlockDiffFormatter($context_lines);
710
711             $this->_content = $fmt->format($diff2);
712             // FIXME: integrate this into class BlockDiffFormatter
713             $this->_content = str_replace(">>>>>>>\n<<<<<<<\n", "=======\n",
714                                           $this->_content);
715             $this->_content = str_replace("<<<<<<<\n>>>>>>>\n", "=======\n",
716                                           $this->_content);
717
718             $this->_currentVersion = $this->current->getVersion();
719             $this->version = $this->_currentVersion;
720             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage();
721         }
722
723         if ($this->editaction == 'preview')
724             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
725
726         // FIXME: NOT_CURRENT_MESSAGE?
727
728         $tokens = array_merge($tokens, $this->getFormElements());
729
730         return $this->output('editpage', _("Merge and Edit: %s"));
731         // FIXME: this doesn't display
732     }
733
734     function output ($template, $title_fs) {
735         $selected = &$this->selected;
736         $current = &$this->current;
737
738         if ($selected && $selected->getVersion() != $current->getVersion()) {
739             $rev = $selected;
740             $pagelink = WikiLink($selected);
741         }
742         else {
743             $rev = $current;
744             $pagelink = WikiLink($this->page);
745         }
746
747         $title = new FormattedText ($title_fs, $pagelink);
748         $template = Template($template, $this->tokens);
749
750         //GeneratePage($template, $title, $rev);
751         PrintXML($template);
752         return true;
753     }
754     function getConflictMessage () {
755         $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.",
756                                     "<<<<<<<",
757                                     "======="),
758                                 HTML::p(_("Please check it through before saving."))));
759         return $message;
760     }
761 }
762
763 /**
764  $Log: not supported by cvs2svn $
765  Revision 1.74  2004/07/03 07:36:28  rurban
766  do not get unneccessary content
767
768  Revision 1.73  2004/06/16 21:23:44  rurban
769  fixed non-object fatal #215
770
771  Revision 1.72  2004/06/14 11:31:37  rurban
772  renamed global $Theme to $WikiTheme (gforge nameclash)
773  inherit PageList default options from PageList
774    default sortby=pagename
775  use options in PageList_Selectable (limit, sortby, ...)
776  added action revert, with button at action=diff
777  added option regex to WikiAdminSearchReplace
778
779  Revision 1.71  2004/06/03 18:06:29  rurban
780  fix file locking issues (only needed on write)
781  fixed immediate LANG and THEME in-session updates if not stored in prefs
782  advanced editpage toolbars (search & replace broken)
783
784  Revision 1.70  2004/06/02 20:47:47  rurban
785  dont use the wikiaction class
786
787  Revision 1.69  2004/06/02 10:17:56  rurban
788  integrated search/replace into toolbar
789  added save+preview buttons
790
791  Revision 1.68  2004/06/01 15:28:00  rurban
792  AdminUser only ADMIN_USER not member of Administrators
793  some RateIt improvements by dfrankow
794  edit_toolbar buttons
795
796  Revision _1.6  2004/05/26 15:48:00  syilek
797  fixed problem with creating page with slashes from one true page
798
799  Revision _1.5  2004/05/25 16:51:53  syilek
800  added ability to create a page from the category page and not have to edit it
801
802  Revision 1.67  2004/05/27 17:49:06  rurban
803  renamed DB_Session to DbSession (in CVS also)
804  added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
805  remove leading slash in error message
806  added force_unlock parameter to File_Passwd (no return on stale locks)
807  fixed adodb session AffectedRows
808  added FileFinder helpers to unify local filenames and DATA_PATH names
809  editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
810
811  Revision 1.66  2004/04/29 23:25:12  rurban
812  re-ordered locale init (as in 1.3.9)
813  fixed loadfile with subpages, and merge/restore anyway
814    (sf.net bug #844188)
815
816  Revision 1.65  2004/04/18 01:11:52  rurban
817  more numeric pagename fixes.
818  fixed action=upload with merge conflict warnings.
819  charset changed from constant to global (dynamic utf-8 switching)
820
821  Revision 1.64  2004/04/06 19:48:56  rurban
822  temp workaround for action=edit AddComment form
823
824  Revision 1.63  2004/03/24 19:39:02  rurban
825  php5 workaround code (plus some interim debugging code in XmlElement)
826    php5 doesn't work yet with the current XmlElement class constructors,
827    WikiUserNew does work better than php4.
828  rewrote WikiUserNew user upgrading to ease php5 update
829  fixed pref handling in WikiUserNew
830  added Email Notification
831  added simple Email verification
832  removed emailVerify userpref subclass: just a email property
833  changed pref binary storage layout: numarray => hash of non default values
834  print optimize message only if really done.
835  forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string.
836    prefs should be stored in db or homepage, besides the current session.
837
838  Revision 1.62  2004/03/17 18:41:05  rurban
839  initial_content and template support for CreatePage
840
841  Revision 1.61  2004/03/12 20:59:17  rurban
842  important cookie fix by Konstantin Zadorozhny
843  new editpage feature: JS_SEARCHREPLACE
844
845  Revision 1.60  2004/02/15 21:34:37  rurban
846  PageList enhanced and improved.
847  fixed new WikiAdmin... plugins
848  editpage, Theme with exp. htmlarea framework
849    (htmlarea yet committed, this is really questionable)
850  WikiUser... code with better session handling for prefs
851  enhanced UserPreferences (again)
852  RecentChanges for show_deleted: how should pages be deleted then?
853
854  Revision 1.59  2003/12/07 20:35:26  carstenklapp
855  Bugfix: Concurrent updates broken since after 1.3.4 release: Fatal
856  error: Call to undefined function: gettransformedcontent() in
857  /home/groups/p/ph/phpwiki/htdocs/phpwiki2/lib/editpage.php on line
858  205.
859
860  Revision 1.58  2003/03/10 18:25:22  dairiki
861  Bug/typo fix.  If you use the edit page to un/lock a page, it
862  failed with: Fatal error: Call to a member function on a
863  non-object in editpage.php on line 136
864
865  Revision 1.57  2003/02/26 03:40:22  dairiki
866  New action=create.  Essentially the same as action=edit, except that if the
867  page already exists, it falls back to action=browse.
868
869  This is for use in the "question mark" links for unknown wiki words
870  to avoid problems and confusion when following links from stale pages.
871  (If the "unknown page" has been created in the interim, the user probably
872  wants to view the page before editing it.)
873
874  Revision 1.56  2003/02/21 18:07:14  dairiki
875  Minor, nitpicky, currently inconsequential changes.
876
877  Revision 1.55  2003/02/21 04:10:58  dairiki
878  Fixes for new cached markup.
879  Some minor code cleanups.
880
881  Revision 1.54  2003/02/16 19:47:16  dairiki
882  Update WikiDB timestamp when editing or deleting pages.
883
884  Revision 1.53  2003/02/15 23:20:27  dairiki
885  Redirect back to browse current version of page upon save,
886  even when no changes were made.
887
888  Revision 1.52  2003/01/03 22:22:00  carstenklapp
889  Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
890
891  Revision 1.51  2003/01/03 02:43:26  carstenklapp
892  New class LoadFileConflictPageEditor, for merging / comparing a loaded
893  pgsrc file with an existing page.
894
895  */
896
897 // Local Variables:
898 // mode: php
899 // tab-width: 8
900 // c-basic-offset: 4
901 // c-hanging-comment-ender-p: nil
902 // indent-tabs-mode: nil
903 // End:
904 ?>