/[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.7 - (show annotations) (download)
Sun Feb 1 12:15:56 2004 UTC (22 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.6: +48 -27 lines
Use try..catch for WikiDB

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24