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