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