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