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