]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
renamed global $Theme to $WikiTheme (gforge nameclash)
[SourceForge/phpwiki.git] / lib / editpage.php
1 <?php
2 rcs_id('$Id: editpage.php,v 1.72 2004-06-14 11:31:37 rurban Exp $');
3
4 require_once('lib/Template.php');
5
6 // USE_HTMLAREA - 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();
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->selected = $this->current;
45             $this->version = $this->current->getVersion();
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         $saveFailed = false;
72         $tokens = &$this->tokens;
73
74         if (! $this->canEdit()) {
75             if ($this->isInitialEdit())
76                 return $this->viewSource();
77             $tokens['PAGE_LOCKED_MESSAGE'] = $this->getLockedMessage();
78         }
79         elseif ($this->request->getArg('save_and_redirect_to') != "") {
80             if ($this->savePage()) {
81                 // noreturn
82                 $this->request->redirect(WikiURL($this->request->getArg('save_and_redirect_to')));
83                 return true;    // Page saved.
84             }
85             $saveFailed = true;
86         }
87         elseif ($this->editaction == 'save') {
88             if ($this->savePage()) {
89                 return true;    // Page saved.
90             }
91             $saveFailed = true;
92         }
93
94         if ($saveFailed || $this->isConcurrentUpdate())
95         {
96             // Get the text of the original page, and the two conflicting edits
97             // The diff3 class takes arrays as input.  So retrieve content as
98             // an array, or convert it as necesary.
99             $orig = $this->page->getRevision($this->_currentVersion);
100             // FIXME: what if _currentVersion has be deleted?
101             $orig_content = $orig->getContent();
102             $this_content = explode("\n", $this->_content);
103             $other_content = $this->current->getContent();
104             include_once("lib/diff3.php");
105             $diff = new diff3($orig_content, $this_content, $other_content);
106             $output = $diff->merged_output(_("Your version"), _("Other version"));
107             // Set the content of the textarea to the merged diff
108             // output, and update the version
109             $this->_content = implode ("\n", $output);
110             $this->_currentVersion = $this->current->getVersion();
111             $this->version = $this->_currentVersion;
112             $unresolved = $diff->ConflictingBlocks;
113             $tokens['CONCURRENT_UPDATE_MESSAGE'] = $this->getConflictMessage($unresolved);
114         }
115
116         if ($this->editaction == 'preview')
117             $tokens['PREVIEW_CONTENT'] = $this->getPreview(); // FIXME: convert to _MESSAGE?
118
119         // FIXME: NOT_CURRENT_MESSAGE?
120         $tokens = array_merge($tokens, $this->getFormElements());
121
122         //FIXME: enable Undo button for all other buttons also, not only the search/replace button
123         if (defined('JS_SEARCHREPLACE') and JS_SEARCHREPLACE) {
124             global $WikiTheme;
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.71  2004/06/03 18:06:29  rurban
766  fix file locking issues (only needed on write)
767  fixed immediate LANG and THEME in-session updates if not stored in prefs
768  advanced editpage toolbars (search & replace broken)
769
770  Revision 1.70  2004/06/02 20:47:47  rurban
771  dont use the wikiaction class
772
773  Revision 1.69  2004/06/02 10:17:56  rurban
774  integrated search/replace into toolbar
775  added save+preview buttons
776
777  Revision 1.68  2004/06/01 15:28:00  rurban
778  AdminUser only ADMIN_USER not member of Administrators
779  some RateIt improvements by dfrankow
780  edit_toolbar buttons
781
782  Revision _1.6  2004/05/26 15:48:00  syilek
783  fixed problem with creating page with slashes from one true page
784
785  Revision _1.5  2004/05/25 16:51:53  syilek
786  added ability to create a page from the category page and not have to edit it
787
788  Revision 1.67  2004/05/27 17:49:06  rurban
789  renamed DB_Session to DbSession (in CVS also)
790  added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
791  remove leading slash in error message
792  added force_unlock parameter to File_Passwd (no return on stale locks)
793  fixed adodb session AffectedRows
794  added FileFinder helpers to unify local filenames and DATA_PATH names
795  editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
796
797  Revision 1.66  2004/04/29 23:25:12  rurban
798  re-ordered locale init (as in 1.3.9)
799  fixed loadfile with subpages, and merge/restore anyway
800    (sf.net bug #844188)
801
802  Revision 1.65  2004/04/18 01:11:52  rurban
803  more numeric pagename fixes.
804  fixed action=upload with merge conflict warnings.
805  charset changed from constant to global (dynamic utf-8 switching)
806
807  Revision 1.64  2004/04/06 19:48:56  rurban
808  temp workaround for action=edit AddComment form
809
810  Revision 1.63  2004/03/24 19:39:02  rurban
811  php5 workaround code (plus some interim debugging code in XmlElement)
812    php5 doesn't work yet with the current XmlElement class constructors,
813    WikiUserNew does work better than php4.
814  rewrote WikiUserNew user upgrading to ease php5 update
815  fixed pref handling in WikiUserNew
816  added Email Notification
817  added simple Email verification
818  removed emailVerify userpref subclass: just a email property
819  changed pref binary storage layout: numarray => hash of non default values
820  print optimize message only if really done.
821  forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string.
822    prefs should be stored in db or homepage, besides the current session.
823
824  Revision 1.62  2004/03/17 18:41:05  rurban
825  initial_content and template support for CreatePage
826
827  Revision 1.61  2004/03/12 20:59:17  rurban
828  important cookie fix by Konstantin Zadorozhny
829  new editpage feature: JS_SEARCHREPLACE
830
831  Revision 1.60  2004/02/15 21:34:37  rurban
832  PageList enhanced and improved.
833  fixed new WikiAdmin... plugins
834  editpage, Theme with exp. htmlarea framework
835    (htmlarea yet committed, this is really questionable)
836  WikiUser... code with better session handling for prefs
837  enhanced UserPreferences (again)
838  RecentChanges for show_deleted: how should pages be deleted then?
839
840  Revision 1.59  2003/12/07 20:35:26  carstenklapp
841  Bugfix: Concurrent updates broken since after 1.3.4 release: Fatal
842  error: Call to undefined function: gettransformedcontent() in
843  /home/groups/p/ph/phpwiki/htdocs/phpwiki2/lib/editpage.php on line
844  205.
845
846  Revision 1.58  2003/03/10 18:25:22  dairiki
847  Bug/typo fix.  If you use the edit page to un/lock a page, it
848  failed with: Fatal error: Call to a member function on a
849  non-object in editpage.php on line 136
850
851  Revision 1.57  2003/02/26 03:40:22  dairiki
852  New action=create.  Essentially the same as action=edit, except that if the
853  page already exists, it falls back to action=browse.
854
855  This is for use in the "question mark" links for unknown wiki words
856  to avoid problems and confusion when following links from stale pages.
857  (If the "unknown page" has been created in the interim, the user probably
858  wants to view the page before editing it.)
859
860  Revision 1.56  2003/02/21 18:07:14  dairiki
861  Minor, nitpicky, currently inconsequential changes.
862
863  Revision 1.55  2003/02/21 04:10:58  dairiki
864  Fixes for new cached markup.
865  Some minor code cleanups.
866
867  Revision 1.54  2003/02/16 19:47:16  dairiki
868  Update WikiDB timestamp when editing or deleting pages.
869
870  Revision 1.53  2003/02/15 23:20:27  dairiki
871  Redirect back to browse current version of page upon save,
872  even when no changes were made.
873
874  Revision 1.52  2003/01/03 22:22:00  carstenklapp
875  Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
876
877  Revision 1.51  2003/01/03 02:43:26  carstenklapp
878  New class LoadFileConflictPageEditor, for merging / comparing a loaded
879  pgsrc file with an existing page.
880
881  */
882
883 // Local Variables:
884 // mode: php
885 // tab-width: 8
886 // c-basic-offset: 4
887 // c-hanging-comment-ender-p: nil
888 // indent-tabs-mode: nil
889 // End:
890 ?>