1 |
#?SuikaWikiConfig/2.0 |
2 |
|
3 |
Plugin: |
4 |
@Name: WikiEdit |
5 |
@Description: |
6 |
@@@: WikiPage editing components |
7 |
@@lang:en |
8 |
@License: %%Perl%% |
9 |
@Author: |
10 |
@@Name: |
11 |
@@@@: Wakaba |
12 |
@@@lang:ja |
13 |
@@@script:Latn |
14 |
@@Mail[list]: w@suika.fam.cx |
15 |
@Date.RCS: |
16 |
$Date: 2004/07/25 06:54:29 $ |
17 |
@RequiredModule[list]: |
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 |
@Namespace: |
30 |
@@edit: |
31 |
http://suika.fam.cx/~wakaba/-temp/2004/7/25/sw-edit# |
32 |
@@media-type: |
33 |
http://suika.fam.cx/~wakaba/-temp/2004/04/24/mt# |
34 |
|
35 |
PluginConst: |
36 |
@NS_XHTML1: |
37 |
http://www.w3.org/1999/xhtml |
38 |
|
39 |
ViewDefinition: |
40 |
@Mode: edit |
41 |
@Condition: |
42 |
@@http-method[list]: |
43 |
GET |
44 |
HEAD |
45 |
@Description: |
46 |
@@@: Edit WikiPage content as whole, as a plaintext |
47 |
@@lang: en |
48 |
@template: |
49 |
@@http-status-code: 200 |
50 |
@@media-type: text/html |
51 |
@@use-media-type-charset: 1 |
52 |
@@expires: %%edit%% |
53 |
@@body: |
54 |
%html-document ( |
55 |
title => {%res(name=>{Edit:WebPageTitle});}p, |
56 |
link-meta => {%template (name => links); |
57 |
%html-meta(name => ROBOTS, content => NOINDEX);}p, |
58 |
content => { |
59 |
%template ( |
60 |
name => ws--page, |
61 |
-content => {%template (name => we--edit-body);}, |
62 |
); |
63 |
}p, |
64 |
); |
65 |
|
66 |
ViewDefinition: |
67 |
@Mode: write |
68 |
@Condition: |
69 |
@@http-method[list]: |
70 |
POST |
71 |
@Description: |
72 |
@@@: Saving modified (new) WikiPage content |
73 |
@@lang: en |
74 |
@Use: |
75 |
use Message::Util::Error; |
76 |
@method: |
77 |
@@@: |
78 |
my $wiki = $self->{view}->{wiki}; |
79 |
my $touch = $wiki->{input}->parameter ('we--touch'); |
80 |
|
81 |
$wiki->{var}->{db}->{read_only}->{'content'} = 0; |
82 |
$wiki->{var}->{db}->{read_only}->{'content_prop'} = 0; |
83 |
$wiki->{var}->{db}->{read_only}->{'lastmodified'} = 0 |
84 |
if $touch; |
85 |
$wiki->{var}->{db}->{read_only}->{'referer'} = 0; |
86 |
$wiki->init_db; |
87 |
|
88 |
my $page = $wiki->{var}->{page}; |
89 |
|
90 |
## Check confliction |
91 |
my $current_content; |
92 |
try { |
93 |
$current_content = $self->{view}->{wiki}->{db}->get (content => $page); |
94 |
} catch SuikaWiki::DB::Util::Error with { |
95 |
my $err = shift; |
96 |
if ($err->{-type} eq 'ERROR_REPORTED') { |
97 |
$err->throw; |
98 |
} else { |
99 |
$self->{view}->{wiki}->view_in_mode (mode => '-wdb--fatal-error'); |
100 |
throw SuikaWiki::DB::Util::Error -type => 'ERROR_REPORTED'; |
101 |
} |
102 |
}; |
103 |
|
104 |
my $current_digest = ''; |
105 |
if (length $current_content) { |
106 |
$current_digest = __FUNCPACK__->digest ($current_content, |
107 |
normalize => 1); |
108 |
my $prev_digest |
109 |
= $self->{view}->{wiki}->{input}->parameter ('we--digest'); |
110 |
if ($current_digest ne $prev_digest) { |
111 |
return $self->{view}->{wiki}->view_in_mode |
112 |
(mode => '-conflict'); |
113 |
} |
114 |
} |
115 |
|
116 |
my $new_content |
117 |
= $self->{view}->{wiki}->{input}->parameter ('we--content'); |
118 |
if (length $new_content) { |
119 |
## Really modified? |
120 |
my $new_digest = __FUNCPACK__->digest ($new_content, normalize => 1); |
121 |
if ($current_digest ne $new_digest) { |
122 |
|
123 |
## Update content |
124 |
try { |
125 |
$self->{view}->{wiki}->{db}->set (content => $page => $new_content); |
126 |
} catch SuikaWiki::DB::Util::Error with { |
127 |
my $err = shift; |
128 |
if ($err->{-type} eq 'ERROR_REPORTED') { |
129 |
$err->throw; |
130 |
} else { |
131 |
$self->{view}->{wiki}->view_in_mode (mode => '-wdb--fatal-error'); |
132 |
throw SuikaWiki::DB::Util::Error -type => 'ERROR_REPORTED'; |
133 |
} |
134 |
}; |
135 |
|
136 |
my $mt = $wiki->{input}->parameter ('wf--media-type'); |
137 |
my $prop = $wiki->{db}->get (content_prop => $page); |
138 |
my $old_mt = $prop->get_attribute_value (<Q:media-type:media-type>, |
139 |
default => undef); |
140 |
UPDATE_MEDIATYPE: { |
141 |
last UPDATE_MEDIATYPE if not $mt or $mt eq '#asis'; |
142 |
CHECK_MEDIATYPE: { |
143 |
for (keys %{$wiki->{config} |
144 |
->{<Q:media-type:accept-media-type>}||{}}) { |
145 |
last CHECK_MEDIATYPE if $mt eq $_; |
146 |
} |
147 |
undef $mt; |
148 |
last UPDATE_MEDIATYPE; |
149 |
} |
150 |
|
151 |
$prop->set_attribute (<Q:media-type:media-type> => $mt); |
152 |
$wiki->{db}->set (content_prop => $page => $prop); |
153 |
} |
154 |
undef $mt if $mt eq '#asis'; |
155 |
|
156 |
## Post-write events |
157 |
if ($old_mt and $old_mt ne $mt) { |
158 |
my $format = $wiki->{plugin}->module_package ('WikiFormat') |
159 |
->handler (\$new_content, |
160 |
serialized_media_type => $old_mt, |
161 |
content_prop => $prop, |
162 |
wiki => $wiki); |
163 |
$format->content_type_changed_from (wiki => $wiki, |
164 |
page => $page, |
165 |
old_content => \$current_content, |
166 |
new_content => \$new_content, |
167 |
content_prop => $prop); |
168 |
} |
169 |
my $format = $wiki->{plugin}->module_package ('WikiFormat') |
170 |
->handler (\$new_content, |
171 |
serialized_media_type => $mt, |
172 |
content_prop => $prop, |
173 |
wiki => $wiki); |
174 |
$format->content_written (wiki => $wiki, |
175 |
page => $page, |
176 |
old_content => \$current_content, |
177 |
new_content => \$new_content, |
178 |
content_prop => $prop); |
179 |
|
180 |
## Update lastmodified |
181 |
if ($touch) { |
182 |
try { |
183 |
$self->{view}->{wiki}->{db}->set (lastmodified => $page => time); |
184 |
} catch SuikaWiki::DB::Util::Error with { |
185 |
my $err = shift; |
186 |
$err->throw if $err->{-type} eq 'ERROR_REPORTED'; |
187 |
}; |
188 |
} |
189 |
} # really modified |
190 |
|
191 |
my $new_mode = $self->{view}->{wiki}->{input}->parameter |
192 |
('we--mode-modified'); |
193 |
$new_mode =~ s/[^0-9A-Za-z._-]+//g; |
194 |
my $uri = $self->{view}->{wiki}->uri_reference |
195 |
(page => $page, |
196 |
mode => $new_mode, |
197 |
up_to_date => 1, |
198 |
param => {we__mode_modified => $new_mode}); |
199 |
|
200 |
require SuikaWiki::Output::HTTP; |
201 |
my $output = SuikaWiki::Output::HTTP->new |
202 |
(wiki => $self->{view}->{wiki}); |
203 |
$output->set_redirect (uri => $uri, status_code => 303); |
204 |
$output->output (output => 'http-cgi'); |
205 |
} else { |
206 |
my $prop = $wiki->{db}->get (content_prop => $page); |
207 |
|
208 |
try { |
209 |
$self->{view}->{wiki}->{db}->delete (content => $page); |
210 |
$self->{view}->{wiki}->{db}->delete (content_prop => $page); |
211 |
$self->{view}->{wiki}->{db}->delete (lastmodified => $page) if $touch; |
212 |
} catch SuikaWiki::DB::Util::Error with { |
213 |
my $err = shift; |
214 |
if ($err->{-type} eq 'ERROR_REPORTED') { |
215 |
$err->throw; |
216 |
} else { |
217 |
$self->{view}->{wiki}->view_in_mode (mode => '-wdb--fatal-error'); |
218 |
throw SuikaWiki::DB::Util::Error -type => 'ERROR_REPORTED'; |
219 |
} |
220 |
}; |
221 |
|
222 |
## Post-write event |
223 |
my $format = $wiki->{plugin}->module_package ('WikiFormat') |
224 |
->handler (\$new_content, |
225 |
content_prop => $prop, |
226 |
wiki => $wiki); |
227 |
$format->content_removed (wiki => $wiki, |
228 |
page => $page, |
229 |
content_prop => $prop); |
230 |
|
231 |
$self->{view}->{wiki}->view_in_mode (mode => '-deleted'); |
232 |
} |
233 |
@@Name: main |
234 |
|
235 |
ViewDefinition: |
236 |
@Mode: adminedit |
237 |
@Condition: |
238 |
@@http-method[list]: |
239 |
GET |
240 |
HEAD |
241 |
@Description: |
242 |
@@@: Edit WikiPage content as whole, as a plaintext (administrator mode) |
243 |
@@lang: en |
244 |
@template: |
245 |
@@http-status-code: 200 |
246 |
@@media-type: application/xhtml+xml |
247 |
@@use-media-type-charset: 1 |
248 |
@@expires: %%edit%% |
249 |
@@body: |
250 |
%html-document ( |
251 |
title => {%res(name=>{Edit:Admin:WebPageTitle});}p, |
252 |
link-meta => {%template (name => links); |
253 |
%html-meta(name=>ROBOTS,content=>NOINDEX);}p, |
254 |
content => { |
255 |
%template ( |
256 |
name => ws--page, |
257 |
-content => {%template (name => we--adminedit-body);}, |
258 |
); |
259 |
}p, |
260 |
); |
261 |
|
262 |
ViewDefinition: |
263 |
@Mode: -conflict |
264 |
@Condition: |
265 |
@@http-method[list]: |
266 |
GET |
267 |
HEAD |
268 |
@Description: |
269 |
@@@: Confliction message |
270 |
@@lang: en |
271 |
@template: |
272 |
@@http-status-code: 409 |
273 |
@@media-type: text/html |
274 |
@@use-media-type-charset: 1 |
275 |
@@body: |
276 |
%html-document ( |
277 |
title => {%res(name=>{Edit:Conflict:WebPageTitle});}p, |
278 |
link-meta => {%template(name=>links); |
279 |
%html-meta(name=>ROBOTS,content=>NOINDEX);}p, |
280 |
content => { |
281 |
%template ( |
282 |
name => ws--page, |
283 |
-content => { |
284 |
%section ( |
285 |
title => {%res(name=>{Edit:Conflict:Title});}p, heading, |
286 |
content => {%template (name => we--conflict-body);}p, |
287 |
); |
288 |
}, |
289 |
); |
290 |
}p, |
291 |
); |
292 |
|
293 |
ViewDefinition: |
294 |
@Mode: -deleted |
295 |
@Condition: |
296 |
@@http-method[list]: |
297 |
GET |
298 |
HEAD |
299 |
@Expires:%%view%% |
300 |
@Description: |
301 |
@@@: Confliction message |
302 |
@@lang: en |
303 |
@template: |
304 |
@@http-status-code: 200 |
305 |
@@media-type: text/html |
306 |
@@use-media-type-charset: 1 |
307 |
@@body: |
308 |
%html-document ( |
309 |
title => {%res (name => {Edit:Deleted:WebPageTitle});}p, |
310 |
link-meta => {%template (name => links); |
311 |
%html-meta (name => ROBOTS, content => NOINDEX);}p, |
312 |
content => { |
313 |
%template ( |
314 |
name => ws--page, |
315 |
-content => { |
316 |
%section ( |
317 |
title => {%res(name=>{Edit:Deleted:Title});}p, heading, |
318 |
content => { |
319 |
%paragraph (content => {%res |
320 |
(name => {Edit:Deleted:Description});}p); |
321 |
}p, |
322 |
); |
323 |
}, |
324 |
); |
325 |
}p, |
326 |
); |
327 |
|
328 |
ViewFragment: |
329 |
@Name: links |
330 |
@Description: |
331 |
@@@: Link to edit mode of the WikiPage |
332 |
@@lang:en |
333 |
@Formatting: |
334 |
%link-wiki(mode => edit, up-to-date, |
335 |
rel => edit, class => wiki-cmd, |
336 |
description => {%res(name=>{Link:Edit:Description});}p); |
337 |
%link-wiki(mode => adminedit, up-to-date, |
338 |
rel=>edit, class => wiki-cmd, |
339 |
description => {%res(name=>{Link:AdminEdit:Description});}p); |
340 |
|
341 |
ViewFragment: |
342 |
@Name: navbar |
343 |
@Description: |
344 |
@@@: Link to edit mode of the WikiPage |
345 |
@Order: 10 |
346 |
@Formatting: |
347 |
%link-to-wikipage ( |
348 |
mode => edit, |
349 |
rel => edit, |
350 |
up-to-date, |
351 |
label => {%link-to-it ( |
352 |
class => wiki-cmd, |
353 |
label => {%res (name => EditThisPage);}p, |
354 |
description => {%res (name => EditThisPageLong);}p, |
355 |
);}, |
356 |
page-anchor-name => edit, |
357 |
); |
358 |
|
359 |
ViewFragment: |
360 |
@Template[list]: |
361 |
we--conflict-body |
362 |
@Order: 30 |
363 |
@Description: |
364 |
@@@: Conflict report |
365 |
@@lang:en |
366 |
@Formatting: |
367 |
%paragraph (content=>{%res(name=>{Edit:Conflict:Description});}p); |
368 |
%we--conflict-modified-content; |
369 |
%section ( |
370 |
id => edit-conflict-diff, |
371 |
title => {%res(name=>{Edit:Conflict:Diff:Title});}p, heading, |
372 |
content => { |
373 |
%paragraph ( |
374 |
content => {%res(name=>{Edit:Conflict:Diff:Description});}p); |
375 |
%conflict-diff; |
376 |
}p, |
377 |
); |
378 |
|
379 |
ViewFragment: |
380 |
@Template[list]: we--edit-body |
381 |
@Order:80 |
382 |
@Description: |
383 |
@@@: Editing WikiPage form section |
384 |
@@lang:en |
385 |
@Formatting: |
386 |
%section ( |
387 |
id => edit, |
388 |
title => {%res (name => {Edit:Title});}p, heading, |
389 |
content => {%edit-form;}p, |
390 |
); |
391 |
|
392 |
ViewFragment: |
393 |
@Template[list]: we--adminedit-body |
394 |
@Order:80 |
395 |
@Description: |
396 |
@@@: Editing WikiPage form section (Admin edit mode) |
397 |
@@lang:en |
398 |
@Formatting: |
399 |
%section ( |
400 |
id => edit, |
401 |
title => {%res(name=>{Edit:Title});}p, heading, |
402 |
content => {%edit-form(admin);}p, |
403 |
); |
404 |
|
405 |
ViewFragment: |
406 |
@Template[list]: we--conflict-body |
407 |
@Order: 80 |
408 |
@Description: |
409 |
@@@: Editing-WikiPage-form section (conflict mode) |
410 |
@@lang:en |
411 |
@Formatting: |
412 |
%section ( |
413 |
id => edit, |
414 |
title => {%res(name=>{Edit:Title});}p, heading, |
415 |
content => { |
416 |
%paragraph(name=>{%res(name=>{Edit:Conflict:Edit:Description});}p); |
417 |
%edit-form; |
418 |
}p, |
419 |
); |
420 |
|
421 |
ViewFragment: |
422 |
@Name: we--edit |
423 |
@Description: |
424 |
@@@: Edit form --- main textarea |
425 |
@@lang:en |
426 |
@Order: 0 |
427 |
@Formatting: |
428 |
%textarea (id => we--content, size => {%res (name=>{Edit:Form:Size});}p, |
429 |
source => content, lines => {%res (name=>{Edit:Form:Lines});}p); |
430 |
ViewFragment: |
431 |
@Name: we--edit |
432 |
@Description: |
433 |
@@@: Submit button |
434 |
@@lang:en |
435 |
@Order: 200 |
436 |
@Formatting: |
437 |
%submit(label=>{%res(name=>{Edit:Save});}p); |
438 |
|
439 |
FormattingRule: |
440 |
@Category[list]: view |
441 |
@Name: edit-form |
442 |
@Description: |
443 |
@@@: Provides WikiPage editing form |
444 |
@@lang: en |
445 |
@Parameter: |
446 |
@@Name: admin |
447 |
@@Type: boolean |
448 |
@@Default: {0} |
449 |
@@Description: |
450 |
@@@@: Whether administrator's editing mode or not |
451 |
@@@lang:en |
452 |
@Parameter: |
453 |
@@Name: page |
454 |
@@Type: WikiName |
455 |
@@Default: (auto) |
456 |
@@Description: |
457 |
@@@@: WikiPage that is editing |
458 |
@@@lang:en |
459 |
@Formatting: |
460 |
__ATTRTEXT:%admin__;__ATTRTEXT:%page__; |
461 |
my $page = $o->{wiki}->name ($p->{page} || $o->{wiki}->{var}->{page}); |
462 |
my $template = $o->{wiki}->{view}->assemble_template ('we__edit'); |
463 |
local $o->{var} = { |
464 |
#content |
465 |
is_admin_mode => $p->{admin}, |
466 |
is_conflict_mode => 0, |
467 |
#is_new_page_template |
468 |
}; |
469 |
my $content = __FUNCPACK__->get_content ($o, $page); |
470 |
$o->{var}->{content} = $content->{content}; |
471 |
$o->{var}->{content_prop} = $content->{content_prop}; |
472 |
SuikaWiki::Plugin->module_package ('WikiFormCore') |
473 |
->make_form_in_html |
474 |
($p->{-parent}, $template, |
475 |
wiki => $o->{wiki}, |
476 |
o => $o, |
477 |
index => -1, |
478 |
output => { |
479 |
mode => 'write', |
480 |
page => $page, |
481 |
hidden => sub { |
482 |
my ($hidden, $o) = @_; |
483 |
for ($hidden->append_new_node (type => '#element', |
484 |
namespace_uri => $NS_XHTML1, |
485 |
local_name => 'input')) { |
486 |
$_->set_attribute (type => 'hidden'); |
487 |
$_->set_attribute (name => 'we--digest'); |
488 |
$_->set_attribute (value => ($o->{var}->{is_new_page_template}?'': |
489 |
__FUNCPACK__->digest ($o->{var}->{content}, |
490 |
normalize => 1))); |
491 |
$_->option (use_EmptyElemTag => 1); |
492 |
} |
493 |
}, |
494 |
}); |
495 |
|
496 |
|
497 |
FormattingRule: |
498 |
@Category[list]: view |
499 |
@Name: we--conflict-modified-content |
500 |
@Description: |
501 |
@@@: |
502 |
Modified content that is conflicted with current content |
503 |
@@lang:en |
504 |
@Formatting: |
505 |
$p->{-parent}->append_new_node (type => '#element', |
506 |
namespace_uri => $NS_XHTML1, |
507 |
local_name => 'pre') |
508 |
->append_text (scalar $o->{wiki}->{input}->parameter ('we--content')); |
509 |
|
510 |
FormattingRule: |
511 |
@Category[list]:view |
512 |
@Name: conflict-diff |
513 |
@Description: |
514 |
@@@: |
515 |
Provides marked diff between latest WikiPage content and |
516 |
submitted one |
517 |
@@lang:en |
518 |
@Formatting: |
519 |
my $before = scalar $o->{wiki}->{input}->parameter ('we--content'); |
520 |
my $after; |
521 |
try { |
522 |
$after = $o->{wiki}->{db}->get (content => $o->{wiki}->{var}->{page}); |
523 |
} catch SuikaWiki::DB::Util::Error with { |
524 |
my $err = shift; |
525 |
$err->throw if $err->{-type} eq 'ERROR_REPORTED'; |
526 |
$after = undef; |
527 |
}; |
528 |
__FUNCPACK__->diff_in_html (\$before => \$after, $p->{-parent}); |
529 |
|
530 |
Function: |
531 |
@Name: diff_in_html |
532 |
@Main: |
533 |
my (undef, $before, $after, $parent, %opt) = @_; |
534 |
require Algorithm::Diff; |
535 |
my $diff = $parent->append_new_node (type => '#element', |
536 |
namespace_uri => $NS_XHTML1, |
537 |
local_name => 'pre'); |
538 |
$diff->set_attribute (class => 'diff'); |
539 |
for (Algorithm::Diff::diff |
540 |
([split /\x0D?\x0A/, $$before]=>[split /\x0D?\x0A/, $$after])) { |
541 |
for (@{$_}) { |
542 |
my ($sign, $lineno, $text) = @{$_}; |
543 |
my $line = $diff->append_new_node (type => '#element', |
544 |
namespace_uri => $NS_XHTML1, |
545 |
local_name => ({qw/+ ins - del/}->{$sign}||'span')); |
546 |
|
547 |
$line->set_attribute (class => 'line'); |
548 |
for ($line->append_new_node (type => '#element', |
549 |
namespace_uri => $NS_XHTML1, |
550 |
local_name => 'span')) { |
551 |
$_->set_attribute (class => 'lineno'); |
552 |
$_->append_text ($lineno); |
553 |
} |
554 |
$line->append_text (' '); |
555 |
for ($line->append_new_node (type => '#element', |
556 |
namespace_uri => $NS_XHTML1, |
557 |
local_name => 'span')) { |
558 |
$_->set_attribute (class => 'sign'); |
559 |
$_->append_text ($sign); |
560 |
} |
561 |
$line->append_text (' '); |
562 |
for ($line->append_new_node (type => '#element', |
563 |
namespace_uri => $NS_XHTML1, |
564 |
local_name => 'span')) { |
565 |
$_->set_attribute (class => 'content'); |
566 |
$_->append_text ($text); |
567 |
} |
568 |
$line->append_text ("\n"); |
569 |
} # diff lines |
570 |
} |
571 |
|
572 |
FormattingRule: |
573 |
@Name:mode-after-edit-selection |
574 |
@Category[list]:form-input |
575 |
@Parameter: |
576 |
@@Name: context |
577 |
@@Type: mode-name |
578 |
@@Default: (current) |
579 |
@@Description: |
580 |
@@@@: Type of "edit" mode |
581 |
@@@lang: en |
582 |
@Parameter: |
583 |
@@Name: description |
584 |
@@Type: text |
585 |
@@Default: (none) |
586 |
@@Description: |
587 |
@@@@: Human readable description about this input |
588 |
@@@lang: en |
589 |
@Formatting: |
590 |
my $name = 'we--mode-modified'; |
591 |
my $selected_mode = $o->{wiki}->{input}->parameter ($name); |
592 |
if ($o->{var}->{content_prop} and not $selected_mode) { |
593 |
my $format = __FUNCPACK{WikiFormat}__ |
594 |
->handler (\$o->{var}->{content}, |
595 |
content_prop => $o->{var}->{content_prop}, |
596 |
wiki => $o->{wiki}); |
597 |
__ATTRTEXT:%context__; |
598 |
$selected_mode = $format->prop |
599 |
(<Q:edit:new-mode-->. |
600 |
($p->{context} || $o->{wiki}->{var}->{mode})) |
601 |
|| 'default'; |
602 |
} |
603 |
|
604 |
__ATTRTEXT:%id__;__ATTRNODE:%label__; |
605 |
my $id = __FUNCPACK{WikiFormCore}__ |
606 |
->control_id ($o, |
607 |
local_id => $name); |
608 |
|
609 |
my $has_label = 0; |
610 |
if ($p->{label}->count) { |
611 |
$has_label = 1; |
612 |
for ($p->{-parent}->append_new_node |
613 |
(type => '#element', |
614 |
namespace_uri => $NS_XHTML1, |
615 |
local_name => 'label')) { |
616 |
$_->set_attribute (for => $id->{global_id}); |
617 |
$_->append_node ($p->{label}); |
618 |
} |
619 |
} |
620 |
|
621 |
my $SELECT = $p->{-parent}->append_new_node (type => '#element', |
622 |
namespace_uri => $NS_XHTML1, |
623 |
local_name => 'select'); |
624 |
$SELECT->set_attribute (name => $name); |
625 |
$SELECT->set_attribute (id => $id->{global_id}) if $has_label; |
626 |
__ATTRTEXT:%description__; |
627 |
$SELECT->set_attribute (title => $p->{description}) |
628 |
if length $p->{description}; |
629 |
$SELECT->set_attribute (disabled => 'disabled') if $o->{form}->{disabled}; |
630 |
__ATTRTEXT:%class__; |
631 |
my @class = split /\s+/, $p->{class}; |
632 |
push @class, 'require' if $o->{form}->{require}->{id}->{$p->{id}}; |
633 |
$SELECT->set_attribute (class => join ' ', @class) if @class; |
634 |
my %outputed; |
635 |
for (qw/default read edit/, $selected_mode) { |
636 |
next if $outputed{$_}; |
637 |
$outputed{$_} = 1; |
638 |
for my $OPTION ($SELECT->append_new_node (type => '#element', |
639 |
namespace_uri => $NS_XHTML1, |
640 |
local_name => 'option')) { |
641 |
$OPTION->set_attribute (value => $_); |
642 |
my $label = SuikaWiki::Plugin->module_package ('WikiResource') |
643 |
->append_node (name => 'Edit:SaveAnd:'.$_.':Label', |
644 |
param => $o, |
645 |
wiki => $o->{wiki}); |
646 |
$OPTION->set_attribute (label => $label->inner_text); |
647 |
$OPTION->append_node ($label); |
648 |
$OPTION->set_attribute |
649 |
(title => SuikaWiki::Plugin->module_package ('WikiResource') |
650 |
->append_node (name => 'Edit:SaveAnd:'.$_.':Description', |
651 |
param => $o, |
652 |
wiki => $o->{wiki})->inner_text); |
653 |
$OPTION->set_attribute (selected => 'selected') |
654 |
if $selected_mode eq $_; |
655 |
} |
656 |
} |
657 |
|
658 |
FormattingRule: |
659 |
@Category[list]:form-input |
660 |
@Name:we--update-lastmodified-datetime |
661 |
@Description: |
662 |
@@@: |
663 |
This direction force to update last-modified date-time |
664 |
of the WikiPage modified. |
665 |
\ |
666 |
Allowing user to select whether last-modified date-time should be |
667 |
updated, use another formatting rule provided by WikiFormSelection |
668 |
module, like: |
669 |
\ |
670 |
\ %check (id => we--touch, |
671 |
\ label => {%res (name => {Edit:UpdateTimeStamp});}p, |
672 |
\ description => {%res |
673 |
\ (name => {Edit:UpdateTimeStamp:Description});}p, |
674 |
\ default); |
675 |
\ |
676 |
WikiForm other that editing form might not be affected by this |
677 |
parameter. See also "we--touch" parameter. |
678 |
@@lang:en |
679 |
@Formatting: |
680 |
for ($p->{-parent}->append_new_node |
681 |
(type => '#element', |
682 |
namespace_uri => $NS_XHTML1, |
683 |
local_name => 'input')) { |
684 |
$_->set_attribute (type => 'hidden'); |
685 |
$_->set_attribute (name => 'we--touch'); |
686 |
$_->set_attribute (value => 'on'); |
687 |
$_->option (use_EmptyElemTag => 1); |
688 |
} |
689 |
|
690 |
|
691 |
Resource: |
692 |
@Edit:Conflict:Title: |
693 |
@@@:Confliction detected! |
694 |
@@lang:en |
695 |
@Edit:Conflict:WebPageTitle: |
696 |
@@@:%page-name; (Edit : 409 CONFLICTION) |
697 |
@@lang:en |
698 |
@Edit:Deleted:Description: |
699 |
@@@:Content of WikiPage %page-name; has been removed. |
700 |
@@lang:en |
701 |
@Edit:Deleted:Title: |
702 |
@@@:WikiPage Deleted |
703 |
@@lang:en |
704 |
@Edit:Deleted:WebPageTitle: |
705 |
@@@:%page-name; (Deleted) |
706 |
@@lang:en |
707 |
@Edit:Form:Lines:15 |
708 |
@Edit:Form:Size:18 |
709 |
@Edit:Save: |
710 |
@@@:Save |
711 |
@@lang:en |
712 |
@Edit:SaveAnd:Description: |
713 |
@@@: Select what to do after saving content |
714 |
@@lang: en |
715 |
@Edit:SaveAnd:adminedit:Description: |
716 |
@@@: |
717 |
Save modified content and show content in the edit mode |
718 |
(for administrator) again |
719 |
@@lang:en |
720 |
@Edit:SaveAnd:adminedit:Label: |
721 |
@@@: Editing |
722 |
@@lang:en |
723 |
@Edit:SaveAnd:default:Description: |
724 |
@@@: Save modified content and show content with your default view mode |
725 |
@@lang:en |
726 |
@Edit:SaveAnd:default:Label: |
727 |
@@@: Default view |
728 |
@@lang:en |
729 |
@Edit:SaveAnd:edit:Description: |
730 |
@@@: Save modified content and show content in the edit mode again |
731 |
@@lang:en |
732 |
@Edit:SaveAnd:edit:Label: |
733 |
@@@: Editing |
734 |
@@lang:en |
735 |
@Edit:SaveAnd:read:Description: |
736 |
@@@: Save modified content and show content in the read mode |
737 |
@@lang:en |
738 |
@Edit:SaveAnd:read:Label: |
739 |
@@@: Show Content |
740 |
@@lang:en |
741 |
@EditThisPage: |
742 |
@@@: Edit |
743 |
@@lang:en |
744 |
@EditThisPageLong: |
745 |
@@@: Edit this WikiPage |
746 |
@@lang:en |
747 |
@Edit:WebPageTitle: |
748 |
@@@: %page-name; (Modification) |
749 |
@@lang:en |
750 |
@Edit:Title: |
751 |
@@@: Modification of the WikiPage |
752 |
@@lang:en |
753 |
@Link:AdminEdit:Description: |
754 |
@@@: Edit this WikiPage (administrator's mode) |
755 |
@@lang:en |
756 |
@Link:Edit:Description: |
757 |
@@@: Edit this WikiPage |
758 |
@@lang:en |
759 |
@Mode:adminedit: |
760 |
@@@: Editing (for administrator) |
761 |
@@lang: en |
762 |
@Mode:edit: |
763 |
@@@: Editing |
764 |
@@lang: en |
765 |
|
766 |
Function: |
767 |
@Name:digest |
768 |
@Description: |
769 |
@@@: Compute digest of given string |
770 |
@@lang:en |
771 |
@Main: |
772 |
my (undef, $s, %opt) = @_; |
773 |
require Digest::SHA1; |
774 |
if ($opt{normalize}) { |
775 |
$s =~ s/\x0D\x0A/\x0A/g; |
776 |
$s =~ tr/\x0D/\x0A/; |
777 |
} |
778 |
Digest::SHA1::sha1_base64 ($s); |
779 |
|
780 |
Function: |
781 |
@Name:get_content |
782 |
@Description: |
783 |
@@@: |
784 |
Get WikiPage content to be modified. For new (currently not exist) |
785 |
WikiPage, new page template content is returned. |
786 |
@@lang:en |
787 |
@Main: |
788 |
my (undef, $o, $page) = @_; |
789 |
my $content; |
790 |
my $content_prop; |
791 |
my $original_content_prop; |
792 |
try { |
793 |
$content = $o->{wiki}->{db}->get (content => $page); |
794 |
$content_prop = $o->{wiki}->{db}->get (content_prop => $page); |
795 |
|
796 |
unless (length $content) { |
797 |
## TODO: use namespaced template page |
798 |
$content = $o->{wiki}->{db}->get |
799 |
(content => $o->{wiki}->{config}->{page}->{NewPageTemplate}); |
800 |
$original_content_prop = $content_prop; |
801 |
$content_prop = $o->{wiki}->{db}->get |
802 |
(content_prop => $o->{wiki}->{config}->{page}->{NewPageTemplate}) |
803 |
->clone; |
804 |
$o->{var}->{is_new_page_template} = 1; |
805 |
} |
806 |
} catch SuikaWiki::DB::Util::Error with { |
807 |
my $err = shift; |
808 |
$err->throw if $err->{-type} eq 'ERROR_REPORTED'; |
809 |
$o->{wiki}->view_in_mode (mode => '-wdb--fatal-error'); |
810 |
throw SuikaWiki::DB::Util::Error -type => 'ERROR_REPORTED'; |
811 |
}; |
812 |
|
813 |
if ($original_content_prop) { |
814 |
for (@{$original_content_prop->child_nodes}) { |
815 |
$content_prop->append_node ($_); |
816 |
} |
817 |
} |
818 |
|
819 |
unless ($content_prop->get_attribute_value (<Q:media-type:media-type>, default => undef)) { |
820 |
if ($content =~ m[^(?:\#\?|/\*\s*)([\w.+-]+(?:/[\w.+-]+)?)]) { |
821 |
$content_prop->set_attribute (<Q:media-type:media-type> => 'MAGIC:'.$1.'##'); |
822 |
} else { |
823 |
$content_prop->set_attribute (<Q:media-type:media-type> => 'IMT:text/x-suikawiki;version="0.9"##'); |
824 |
} |
825 |
} |
826 |
|
827 |
{content => $content, content_prop => $content_prop}; |
828 |
|
829 |
Parameter: |
830 |
@Name: we--content |
831 |
@Type: text |
832 |
@Default: "" |
833 |
@Description: |
834 |
@@@: |
835 |
Content of WikiPage (to be) |
836 |
@@lang:en |
837 |
|
838 |
Parameter: |
839 |
@Name: we--digest |
840 |
@Type: "Base64ed SHA1" |
841 |
@Default: {undef} |
842 |
@Description: |
843 |
@@@: |
844 |
SHA1 digest value of the WikiPage content before the modification. |
845 |
This value is used to detect confliction of modifying. |
846 |
Empty value (undef or length-zero-string) means that |
847 |
there were no WikiPage content. |
848 |
@@lang:en |
849 |
|
850 |
Parameter: |
851 |
@Name: we--mode-modified |
852 |
@Type: mode-name |
853 |
@Default: "default" |
854 |
@Description: |
855 |
@@@: |
856 |
WikiEngine running mode to be specified after modification is completed |
857 |
@@lang:en |
858 |
|
859 |
Parameter: |
860 |
@Name: we--touch |
861 |
@Type: |
862 |
form:checkbox-value |
863 |
@Default: "off" |
864 |
@Description: |
865 |
@@@: |
866 |
Whether last-modified date-time of the WikiPage should be updated or not |
867 |
when the WikiPage is modified. |
868 |
@@lang:en |