/[pub]/suikawiki/script/lib/SuikaWiki/Plugin/WikiEdit.wp2
Suika

Contents of /suikawiki/script/lib/SuikaWiki/Plugin/WikiEdit.wp2

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (show annotations) (download)
Tue Nov 25 12:38:22 2003 UTC (22 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.2: +84 -61 lines
Use new WikiPlugin formatting interface

1 #?SuikaWikiConfig/2.0
2
3 Plugin:
4 @Name: WikiEdit
5 @Description:
6 @@@: WikiPage editing components
7 @@lang:en
8 @License: %%GPL%%
9 @Author:
10 @@Name:
11 @@@@: Wakaba
12 @@@lang:ja
13 @@@script:Latn
14 @@Mail[list]: w@suika.fam.cx
15 @Date.RCS: $Date: 2003/10/30 07:45:10 $
16 @RequiredModule[list]:
17 SuikaWiki::Name::Space
18 Digest::SHA1
19 @RequiredPlugin[list]:
20 WikiView
21 WikiStruct
22 WikiLinking
23 WikiFormCore
24 WikiFormText
25 HTML
26
27 PluginConst:
28 @NS_XHTML1:
29 http://www.w3.org/1999/xhtml
30
31 ViewDefinition:
32 @Mode: edit
33 @Condition:
34 @@http-method[list]:
35 GET
36 HEAD
37 @Description:
38 @@@: Edit WikiPage content as whole, as a plaintext
39 @@lang: en
40 @template:
41 @@http-status-code: 200
42 @@media-type: text/html
43 @@use-media-type-charset: 1
44 @@expires: %%edit%%
45 @@body:
46 %html-document (
47 title => {%res(name=>{Edit:WebPageTitle});}p,
48 link-meta => {%template (name => links);
49 %html-meta(name => ROBOTS, content => NOINDEX);}p,
50 content => {
51 %template (
52 name => ws--page,
53 -content => {%template (name => we--edit-body);},
54 );
55 }p,
56 );
57
58 ViewDefinition:
59 @Mode: write
60 @Condition:
61 @@http-method[list]:
62 POST
63 @Description:
64 @@@: Saving modified (new) WikiPage content
65 @@lang: en
66 @method:
67 @@@:
68 my $touch = $self->{view}->{wiki}->{input}->parameter ('we--touch');
69
70 $self->{view}->{wiki}->{var}->{db}->{read_only}->{'content'} = 0;
71 $self->{view}->{wiki}->{var}->{db}->{read_only}->{'lastmodified'} = 0
72 if $touch;
73 $self->{view}->init_db;
74
75 my $page = $self->{view}->{wiki}->{var}->{page};
76
77 ## TODO: Implements access control
78 #if (&frozen_reject()) {
79 #}
80
81 if (not __FUNCPACK__::page_is_editable ($page)) {
82 ## TODO: Implements this
83 # &_do_view_msg (-view => '-error', -page => $main::form{mypage},
84 # error_message => &Resource ('Error:ThisPageIsUneditable'));
85 # return;
86 die "Uneditable!";
87 }
88
89 ## Check confliction
90 my $current_content = $self->{view}->{wiki}->{db}->get (content => $page);
91 if (length $current_content) {
92 my $current_digest = __FUNCPACK__::digest ($current_content,
93 normalize => 1);
94 my $prev_digest
95 = $self->{view}->{wiki}->{input}->parameter ('we--digest');
96 if ($current_digest ne $prev_digest) {
97 ## TODO: Implements this
98 # _do_view_msg (-view => '-conflict', -page => $page_name);
99 # die "@{[join '//', @$page]}: Conflict : (current) $current_digest [$current_content] vs (prev) $prev_digest";
100 return $self->{view}->{wiki}->view_in_mode
101 (mode => '-conflict');
102 }
103 }
104
105 my $new_content
106 = $self->{view}->{wiki}->{input}->parameter ('we--content');
107 if (length $new_content) {
108 $self->{view}->{wiki}->{db}->set (content => $page => $new_content);
109 if ($touch) {
110 $self->{view}->{wiki}->{db}->set (lastmodified => $page => time);
111 }
112
113 ## TODO: Use SuikaWiki 3 interface
114 my $new_mode = $self->{view}->{wiki}->{input}->parameter
115 ('we--mode-modified');
116 $new_mode =~ s/[^0-9A-Za-z._-]+//g;
117 my $uri = SuikaWiki::Plugin->_uri_wiki_page (join ('//', @$page),
118 mode => $new_mode,
119 with_lm => 1,
120 absolute => 1);
121 $uri .= qq<;we--mode-modified=$new_mode>;
122
123 require SuikaWiki::Output::HTTP;
124 my $output = SuikaWiki::Output::HTTP->new
125 (wiki => $self->{view}->{wiki});
126 $output->set_redirect (uri => $uri, status_code => 303);
127 $output->output (output => 'http-cgi');
128 } else {
129 for (qw/content lastmodified/) {
130 $self->{view}->{wiki}->{db}->delete (content => $page);
131 }
132 $self->{view}->{wiki}->view_in_mode (mode => '-deleted');
133 }
134 @@Name: main
135
136 ViewDefinition:
137 @Mode: adminedit
138 @Condition:
139 @@http-method[list]:
140 GET
141 HEAD
142 @Description:
143 @@@: Edit WikiPage content as whole, as a plaintext (administrator mode)
144 @@lang: en
145 @template:
146 @@http-status-code: 200
147 @@media-type: application/xhtml+xml
148 @@use-media-type-charset: 1
149 @@expires: %%edit%%
150 @@body:
151 %html-document (
152 title => {%res(name=>{Edit:Admin:WebPageTitle});}p,
153 link-meta => {%predefined-template(name=>links);
154 %html-meta(name=>ROBOTS,content=>NOINDEX);}p,
155 content => {
156 %template (
157 name => ws--page,
158 -content => {%template (name => we--adminedit-body);},
159 );
160 }p,
161 );
162
163 ViewDefinition:
164 @Mode: -conflict
165 @Condition:
166 @@http-method[list]:
167 GET
168 HEAD
169 @Description:
170 @@@: Confliction message
171 @@lang: en
172 @template:
173 @@http-status-code: 409
174 @@media-type: text/html
175 @@use-media-type-charset: 1
176 @@body:
177 %html-document (
178 title => {%res(name=>{Edit:Conflict:WebPageTitle});}p,
179 link-meta => {%template(name=>links);
180 %html-meta(name=>ROBOTS,content=>NOINDEX);}p,
181 content => {
182 %template (
183 name => ws--page,
184 -content => {
185 %section (level=>2,
186 title => {%res(name=>{Edit:Conflict:Title});}p, heading,
187 content => {%template (name => we--conflict-body);}p,
188 );
189 },
190 );
191 }p,
192 );
193
194 ViewDefinition:
195 @Mode: -deleted
196 @Condition:
197 @@http-method[list]:
198 GET
199 HEAD
200 @Expires:%%view%%
201 @Description:
202 @@@: Confliction message
203 @@lang: en
204 @template:
205 @@http-status-code: 200
206 @@media-type: text/html
207 @@use-media-type-charset: 1
208 @@body:
209 %html-document (
210 title => {%res (name => {Edit:Deleted:WebPageTitle});}p,
211 link-meta => {%template (name => links);
212 %html-meta (name => ROBOTS, content => NOINDEX);}p,
213 content => {
214 %template (
215 name => ws--page,
216 -content => {
217 %section (level=>2,
218 title => {%res(name=>{Edit:Deleted:Title});}p, heading,
219 content => {
220 %paragraph (content => {%res
221 (name => {Edit:Deleted:Description});}p);
222 }p,
223 );
224 },
225 );
226 }p,
227 );
228
229 ViewFragment:
230 @Name: links
231 @Description:
232 @@@: Link to edit mode of the WikiPage
233 @@lang:en
234 @Formatting:
235 %link-wiki(mode=>edit,rel=>edit,class=>wiki-cmd,
236 title=>{%res(name=>{Link:Edit:Description});}p,up-to-date);
237 %link-wiki(mode=>adminedit,rel=>edit,class=>wiki-cmd,
238 title=>{%res(name=>{Link:AdminEdit:Description});}p,up-to-date);
239
240 ViewFragment:
241 @Name: navbar
242 @Description:
243 @@@: Link to edit mode of the WikiPage
244 @Order: 10
245 @Formatting:
246 %link-to-wikipage (
247 mode => edit,
248 rel => edit,
249 up-to-date,
250 label => {%link-to-it (
251 class => wiki-cmd,
252 label => {%res (name => EditThisPage);}p,
253 description => {%res (name => EditThisPageLong);}p,
254 );},
255 page-anchor-name => edit,
256 );
257
258 ViewFragment:
259 @Template[list]:
260 we--conflict-body
261 @Order: 30
262 @Description:
263 @@@: Conflict report
264 @@lang:en
265 @Formatting:
266 %paragraph (content=>{%res(name=>{Edit:Conflict:Description});}p);
267 %we--conflict-modified-content;
268 %section (level=>3,
269 id => edit-conflict-diff,
270 title => {%res(name=>{Edit:Conflict:Diff:Title});}p, heading,
271 content => {
272 %paragraph (
273 content => {%res(name=>{Edit:Conflict:Diff:Description});}p);
274 %conflict-diff;
275 }p,
276 );
277
278 ViewFragment:
279 @Template[list]: we--edit-body
280 @Order:80
281 @Description:
282 @@@: Editing WikiPage form section
283 @@lang:en
284 @Formatting:
285 %section (level=>2,
286 id => edit,
287 title => {%res (name => {Edit:Title});}p, heading,
288 content => {%edit-form;}p,
289 );
290
291 ViewFragment:
292 @Template[list]: we--adminedit-body
293 @Order:80
294 @Description:
295 @@@: Editing WikiPage form section (Admin edit mode)
296 @@lang:en
297 @Formatting:
298 %section (level=>2,
299 id => edit,
300 title => {%res(name=>{Edit:Title});}p, heading,
301 content => {%edit-form(admin);}p,
302 );
303
304 ViewFragment:
305 @Template[list]: we--conflict-body
306 @Order: 80
307 @Description:
308 @@@: Editing-WikiPage-form section (conflict mode)
309 @@lang:en
310 @Formatting:
311 %section (level=>2,
312 id => edit,
313 title => {%res(name=>{Edit:Title});}p, heading,
314 content => {
315 %paragraph(name=>{%res(name=>{Edit:Conflict:Edit:Description});}p);
316 %edit-form;
317 }p,
318 );
319
320 ViewFragment:
321 @Name: we--edit
322 @Order: -1000
323 @Formatting:
324 %block (content => {%form-hiddens;
325 ViewFragment:
326 @Name: we--edit
327 @Description:
328 @@@: Edit form --- main textarea
329 @@lang:en
330 @Order: 0
331 @Formatting:
332 %textarea(id=>we--content,size=>{%res(name=>{Edit:Form:Size});}p,
333 lines=>{%res(name=>{Edit:Form:Lines});}p);
334 ViewFragment:
335 @Name: we--edit
336 @Description:
337 @@@: Submit button
338 @@lang:en
339 @Order: 200
340 @Formatting:
341 %submit(label=>{%res(name=>{Edit:Save});}p);
342 ViewFragment:
343 @Name: we--edit
344 @Order: 1000
345 @Formatting:
346 }p);
347
348 FormattingRule:
349 @Category[list]: view
350 @Name: edit-form
351 @Description:
352 @@@: Provides WikiPage editing form
353 @@lang: en
354 @Parameter:
355 @@Name: admin
356 @@Type: boolean
357 @@Default: {0}
358 @@Description:
359 @@@@: Whether administrator's editing mode or not
360 @@@lang:en
361 @Formatting:
362 __ATTRTEXT:%admin__;
363 if (not __FUNCPACK__::page_is_editable ($o->{wiki}->{var}->{page})) {
364 $p->{-parent}->append_new_node (type => '#element',
365 namespace_uri => $NS_XHTML1,
366 local_name => 'p')
367 ->append_text ($o->resource('Error:ThisPageIsUneditable'));
368 # } elsif (!$p->{admin} && &main::is_frozen ($o->{page})) {
369 # $r = '<p>'.$o->resource('Error:ThisPageIsUneditable',escape=>1).'</p>';
370 } else {
371 #$r = '<p>'.$o->resource('Error:PasswordIsNotSpecified',escape=>1).'</p>' if $p->{admin};
372
373 my $form = $p->{-parent}->append_new_node
374 (type => '#element',
375 namespace_uri => $NS_XHTML1,
376 local_name => 'form');
377 $form->set_attribute (method => 'post');
378 ## TODO: Use SuikaWiki 3 interface
379 $form->set_attribute (action => $o->uri ('wiki'));
380 $form->set_attribute ('accept-charset' => 'iso-2022-jp utf-8');
381
382 my $hidden = ref ($p->{-parent})->new (type => '#fragment');
383 local $o->{form}->{hidden} = $hidden;
384 for ($hidden->append_new_node (type => '#element',
385 namespace_uri => $NS_XHTML1,
386 local_name => 'input')) {
387 $_->set_attribute (type => 'hidden');
388 $_->set_attribute (name => 'mypage');
389 ## TODO: Use SuikaWiki 3 interface
390 $_->set_attribute (value => join '//', @{$o->{wiki}->{var}->{page}});
391 $_->option (use_EmptyElemTag => 1);
392 }
393 for ($hidden->append_new_node (type => '#element',
394 namespace_uri => $NS_XHTML1,
395 local_name => 'input')) {
396 $_->set_attribute (type => 'hidden');
397 $_->set_attribute (name => 'mode');
398 $_->set_attribute (value => 'write');
399 $_->option (use_EmptyElemTag => 1);
400 }
401 ## _charset_ hack
402 for ($hidden->append_new_node (type => '#element',
403 namespace_uri => $NS_XHTML1,
404 local_name => 'input')) {
405 $_->set_attribute (type => 'hidden');
406 $_->set_attribute (name => '_charset_');
407 $_->option (use_EmptyElemTag => 1);
408 }
409
410 my $ovar_orig = $o->{var};
411 $o->{var} = {
412 #content
413 is_admin_mode => $p->{admin},
414 is_conflict_mode => 0,
415 #is_new_page_template
416 };
417 $o->{var}->{content} = __FUNCPACK__::get_content
418 ($o, $o->{wiki}->{var}->{page});
419 for ($hidden->append_new_node (type => '#element',
420 namespace_uri => $NS_XHTML1,
421 local_name => 'input')) {
422 $_->set_attribute (type => 'hidden');
423 $_->set_attribute (name => 'we--digest');
424 $_->set_attribute (value => ($o->{var}->{is_new_page_template}?'':
425 __FUNCPACK__::digest ($o->{var}->{content},
426 normalize => 1)));
427 $_->option (use_EmptyElemTag => 1);
428 }
429
430 $form->append_node ($o->formatter ('form_input')->replace
431 ($o->{wiki}->{view}->assemble_template
432 ('we__edit'),
433 param => $o));
434 $o->{var} = $ovar_orig;
435 }
436
437 FormattingRule:
438 @Category[list]: view
439 @Name: we--conflict-modified-content
440 @Description:
441 @@@:
442 Modified content that is conflicted with current content
443 @@lang:en
444 @Formatting:
445 $p->{-parent}->append_new_node (type => '#element',
446 namespace_uri => $NS_XHTML1,
447 local_name => 'pre')
448 ->append_text (scalar $o->{wiki}->{input}->parameter ('we--content'));
449
450 FormattingRule:
451 @Category[list]:view
452 @Name: conflict-diff
453 @Description:
454 @@@:
455 Provides marked diff between latest WikiPage content and
456 submitted one
457 @@lang:en
458 @Formatting:
459 require Algorithm::Diff;
460 my $diff = $p->{-parent}->append_new_node (type => '#element',
461 namespace_uri => $NS_XHTML1,
462 local_name => 'pre');
463 $diff->set_attribute (class => 'diff');
464 for (Algorithm::Diff::diff (
465 [split /\x0D?\x0A/,
466 scalar $o->{wiki}->{input}->parameter ('we--content')],
467 [split /\x0D?\x0A/,
468 $o->{wiki}->{db}->get (content
469 => $o->{wiki}->{var}->{page})]
470 )) {
471 for (@{$_}) {
472 my ($sign, $lineno, $text) = @{$_};
473 my $ename = $sign eq '+' ? 'ins' : $sign eq '-' ? 'del' : 'span';
474 my $line = $diff->append_new_node (type => '#element',
475 namespace_uri => $NS_XHTML1,
476 local_name => ({qw/+ ins - del/}->{$sign}||'span'));
477
478 $line->set_attribute (class => 'line');
479 for ($line->append_new_node (type => '#element',
480 namespace_uri => $NS_XHTML1,
481 local_name => 'span')) {
482 $_->set_attribute (class => 'lineno');
483 $_->append_text ($lineno);
484 }
485 $line->append_text (' ');
486 for ($line->append_new_node (type => '#element',
487 namespace_uri => $NS_XHTML1,
488 local_name => 'span')) {
489 $_->set_attribute (class => 'sign');
490 $_->append_text ($sign);
491 }
492 $line->append_text (' ');
493 for ($line->append_new_node (type => '#element',
494 namespace_uri => $NS_XHTML1,
495 local_name => 'span')) {
496 $_->set_attribute (class => 'content');
497 $_->append_text ($text);
498 }
499 $line->append_text ("\n");
500 } # diff lines
501 }
502
503 FormattingRule:
504 @Name:mode-after-edit-selection
505 @Category[list]:we--edit
506 @Parameter:
507 @@Name: name
508 @@Type: html4:name
509 @@Default:"we--mode-modified"
510 @@Description:
511 @@@@:Form control name for the selection
512 @@@lang:en
513 @Formatting:
514 __ATTRTEXT:%name__;
515 my $magic = '';
516 $magic = $1 if $o->{var}->{content} =~ m/^([^\x0A\x0D]+)/s;
517
518 my $name = $p->{name} || 'we--mode-modified';
519 my $selected_mode = $o->{wiki}->{input}->parameter ($name);
520 unless ($selected_mode) {
521 if ($magic =~ /C(?:on(?:fig|st)|SS)/) {
522 $selected_mode = 'edit';
523 } else {
524 $selected_mode = 'default';
525 }
526 }
527
528 my $SELECT = $p->{-parent}->append_new_node (type => '#element',
529 namespace_uri => $NS_XHTML1,
530 local_name => 'select');
531 $SELECT->set_attribute (name => $name);
532 for (qw/default read edit/) {
533 for my $OPTION ($SELECT->append_new_node (type => '#element',
534 namespace_uri => $NS_XHTML1,
535 local_name => 'option')) {
536 $OPTION->set_attribute (value => $_);
537 my $label = $o->resource ('Edit:SaveAnd:'.$_.':Label');
538 $OPTION->set_attribute (label => $label);
539 $OPTION->append_text ($label);
540 $OPTION->set_attribute
541 (title => $o->resource ('Edit:SaveAnd:'.$_.':Description'));
542 $OPTION->set_attribute (selected => 'selected')
543 if $selected_mode eq $_;
544 }
545 }
546
547 Resource:
548 @Edit:Conflict:Title:
549 @@@:Confliction detected!
550 @@lang:en
551 @Edit:Conflict:WebPageTitle:
552 @@@:%page-name; (Edit : 409 CONFLICTION)
553 @@lang:en
554 @Edit:Deleted:Description:
555 @@@:Content of WikiPage %page-name; has been removed.
556 @@lang:en
557 @Edit:Deleted:Title:
558 @@@:WikiPage Deleted
559 @@lang:en
560 @Edit:Deleted:WebPageTitle:
561 @@@:%page-name; (Deleted)
562 @@lang:en
563 @Edit:Form:Lines:15
564 @Edit:Form:Size:18
565 @Edit:Save:
566 @@@:Save
567 @@lang:en
568 @Edit:SaveAnd:default:Description:
569 @@@: Save modified content and show content with your default view mode
570 @@lang:en
571 @Edit:SaveAnd:default:Label:
572 @@@: Default view
573 @@lang:en
574 @Edit:SaveAnd:edit:Description:
575 @@@: Save modified content and show content in the edit mode again
576 @@lang:en
577 @Edit:SaveAnd:edit:Label:
578 @@@: Edit again
579 @@lang:en
580 @Edit:SaveAnd:read:Description:
581 @@@: Save modified content and show content in the read mode
582 @@lang:en
583 @Edit:SaveAnd:read:Label:
584 @@@: Show content
585 @@lang:en
586 @EditThisPage:
587 @@@: Edit
588 @@lang:en
589 @EditThisPageLong:
590 @@@: Edit this WikiPage
591 @@lang:en
592 @Edit:WebPageTitle:
593 @@@: %page-name; (Modification)
594 @@lang:en
595 @Edit:Title:
596 @@@: Modification of the WikiPage
597 @@lang:en
598 @Link:AdminEdit:Description:
599 @@@: Edit this WikiPage (administrator's mode)
600 @@lang:en
601 @Link:Edit:Description:
602 @@@: Edit this WikiPage
603 @@lang:en
604
605 Function:
606 @Name:digest
607 @Description:
608 @@@: Compute digest of given string
609 @@lang:en
610 @Main:
611 my ($s, %opt) = @_;
612 require Digest::SHA1;
613 if ($opt{normalize}) {
614 $s =~ s/\x0D\x0A/\x0A/g;
615 $s =~ tr/\x0D/\x0A/;
616 }
617 Digest::SHA1::sha1_base64 ($s);
618
619 Function:
620 @Name:get_content
621 @Description:
622 @@@:Get WikiPage content to be modified. For new (currently not exist)
623 WikiPage, new page template content is returned.
624 @@lang:en
625 @Main:
626 my ($o, $page) = @_;
627 my $content;
628 $content = $o->{wiki}->{db}->get (content => $page);
629
630 unless (length $content) {
631 ## TODO: use namespaced template page
632 $content = $o->{wiki}->{db}->get
633 (content => $o->{wiki}->{config}->{page}->{NewPageTemplate});
634 $o->{var}->{is_new_page_template} = 1;
635 }
636 $content;
637
638 Function:
639 @Name:page_is_editable
640 @Description:
641 @@@:
642 Returns whether the WikiPage is editable or not.
643 Note that this function is temporary.
644 @@lang:en
645 @Main:
646 my ($page) = @_;
647 $page = join '//', $page;
648 return 0 unless SuikaWiki::Name::Space::validate_name ($page);
649 return 0 if $page =~ /[\x00-\x20\[\]\x7F]/;
650 1;
651
652 Parameter:
653 @Name: we--content
654 @Type: text
655 @Default: ""
656 @Description:
657 @@@:
658 Content of WikiPage (to be)
659 @@lang:en
660
661 Parameter:
662 @Name: we--digest
663 @Type: "Base64ed SHA1"
664 @Default: {undef}
665 @Description:
666 @@@:
667 SHA1 digest value of the WikiPage content before the modification.
668 This value is used to detect confliction of modifying.
669 Empty value (undef or length-zero-string) means that
670 there were no WikiPage content.
671 @@lang:en
672
673 Parameter:
674 @Name: we--mode-modified
675 @Type: mode-name
676 @Default: "default"
677 @Description:
678 @@@:
679 WikiEngine running mode to be specified after modification is completed
680 @@lang:en
681
682 Parameter:
683 @Name: we--touch
684 @Type: form:checkbox-value
685 @Default: "off"
686 @Description:
687 @@@:
688 Whether last-modified date-time of the WikiPage should be updated or not
689 when the WikiPage is modified.
690 @@lang:en

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24