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