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