]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/editpage.php
remove final \n to be ob_cache independent
[SourceForge/phpwiki.git] / lib / editpage.php
1 <?php
2 rcs_id('$Id: editpage.php,v 1.79 2004-11-21 11:59:20 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.78  2004/11/16 17:57:45  rurban
807  fix search&replace button
808  use new addTagButton machinery
809  new showPulldown for categories, TODO: in a seperate request
810
811  Revision 1.77  2004/11/15 15:52:35  rurban
812  improve js stability
813
814  Revision 1.76  2004/11/15 15:37:34  rurban
815  fix JS_SEARCHREPLACE
816    don't use document.write for replace, otherwise self.opener is not defined.
817
818  Revision 1.75  2004/09/16 08:00:52  rurban
819  just some comments
820
821  Revision 1.74  2004/07/03 07:36:28  rurban
822  do not get unneccessary content
823
824  Revision 1.73  2004/06/16 21:23:44  rurban
825  fixed non-object fatal #215
826
827  Revision 1.72  2004/06/14 11:31:37  rurban
828  renamed global $Theme to $WikiTheme (gforge nameclash)
829  inherit PageList default options from PageList
830    default sortby=pagename
831  use options in PageList_Selectable (limit, sortby, ...)
832  added action revert, with button at action=diff
833  added option regex to WikiAdminSearchReplace
834
835  Revision 1.71  2004/06/03 18:06:29  rurban
836  fix file locking issues (only needed on write)
837  fixed immediate LANG and THEME in-session updates if not stored in prefs
838  advanced editpage toolbars (search & replace broken)
839
840  Revision 1.70  2004/06/02 20:47:47  rurban
841  dont use the wikiaction class
842
843  Revision 1.69  2004/06/02 10:17:56  rurban
844  integrated search/replace into toolbar
845  added save+preview buttons
846
847  Revision 1.68  2004/06/01 15:28:00  rurban
848  AdminUser only ADMIN_USER not member of Administrators
849  some RateIt improvements by dfrankow
850  edit_toolbar buttons
851
852  Revision _1.6  2004/05/26 15:48:00  syilek
853  fixed problem with creating page with slashes from one true page
854
855  Revision _1.5  2004/05/25 16:51:53  syilek
856  added ability to create a page from the category page and not have to edit it
857
858  Revision 1.67  2004/05/27 17:49:06  rurban
859  renamed DB_Session to DbSession (in CVS also)
860  added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
861  remove leading slash in error message
862  added force_unlock parameter to File_Passwd (no return on stale locks)
863  fixed adodb session AffectedRows
864  added FileFinder helpers to unify local filenames and DATA_PATH names
865  editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
866
867  Revision 1.66  2004/04/29 23:25:12  rurban
868  re-ordered locale init (as in 1.3.9)
869  fixed loadfile with subpages, and merge/restore anyway
870    (sf.net bug #844188)
871
872  Revision 1.65  2004/04/18 01:11:52  rurban
873  more numeric pagename fixes.
874  fixed action=upload with merge conflict warnings.
875  charset changed from constant to global (dynamic utf-8 switching)
876
877  Revision 1.64  2004/04/06 19:48:56  rurban
878  temp workaround for action=edit AddComment form
879
880  Revision 1.63  2004/03/24 19:39:02  rurban
881  php5 workaround code (plus some interim debugging code in XmlElement)
882    php5 doesn't work yet with the current XmlElement class constructors,
883    WikiUserNew does work better than php4.
884  rewrote WikiUserNew user upgrading to ease php5 update
885  fixed pref handling in WikiUserNew
886  added Email Notification
887  added simple Email verification
888  removed emailVerify userpref subclass: just a email property
889  changed pref binary storage layout: numarray => hash of non default values
890  print optimize message only if really done.
891  forced new cookie policy: delete pref cookies, use only WIKI_ID as plain string.
892    prefs should be stored in db or homepage, besides the current session.
893
894  Revision 1.62  2004/03/17 18:41:05  rurban
895  initial_content and template support for CreatePage
896
897  Revision 1.61  2004/03/12 20:59:17  rurban
898  important cookie fix by Konstantin Zadorozhny
899  new editpage feature: JS_SEARCHREPLACE
900
901  Revision 1.60  2004/02/15 21:34:37  rurban
902  PageList enhanced and improved.
903  fixed new WikiAdmin... plugins
904  editpage, Theme with exp. htmlarea framework
905    (htmlarea yet committed, this is really questionable)
906  WikiUser... code with better session handling for prefs
907  enhanced UserPreferences (again)
908  RecentChanges for show_deleted: how should pages be deleted then?
909
910  Revision 1.59  2003/12/07 20:35:26  carstenklapp
911  Bugfix: Concurrent updates broken since after 1.3.4 release: Fatal
912  error: Call to undefined function: gettransformedcontent() in
913  /home/groups/p/ph/phpwiki/htdocs/phpwiki2/lib/editpage.php on line
914  205.
915
916  Revision 1.58  2003/03/10 18:25:22  dairiki
917  Bug/typo fix.  If you use the edit page to un/lock a page, it
918  failed with: Fatal error: Call to a member function on a
919  non-object in editpage.php on line 136
920
921  Revision 1.57  2003/02/26 03:40:22  dairiki
922  New action=create.  Essentially the same as action=edit, except that if the
923  page already exists, it falls back to action=browse.
924
925  This is for use in the "question mark" links for unknown wiki words
926  to avoid problems and confusion when following links from stale pages.
927  (If the "unknown page" has been created in the interim, the user probably
928  wants to view the page before editing it.)
929
930  Revision 1.56  2003/02/21 18:07:14  dairiki
931  Minor, nitpicky, currently inconsequential changes.
932
933  Revision 1.55  2003/02/21 04:10:58  dairiki
934  Fixes for new cached markup.
935  Some minor code cleanups.
936
937  Revision 1.54  2003/02/16 19:47:16  dairiki
938  Update WikiDB timestamp when editing or deleting pages.
939
940  Revision 1.53  2003/02/15 23:20:27  dairiki
941  Redirect back to browse current version of page upon save,
942  even when no changes were made.
943
944  Revision 1.52  2003/01/03 22:22:00  carstenklapp
945  Minor adjustments to diff block markers ("<<<<<<<"). Source reformatting.
946
947  Revision 1.51  2003/01/03 02:43:26  carstenklapp
948  New class LoadFileConflictPageEditor, for merging / comparing a loaded
949  pgsrc file with an existing page.
950
951  */
952
953 // Local Variables:
954 // mode: php
955 // tab-width: 8
956 // c-basic-offset: 4
957 // c-hanging-comment-ender-p: nil
958 // indent-tabs-mode: nil
959 // End:
960 ?>