1 |
#?SuikaWikiConfig/2.0 |
2 |
|
3 |
Plugin: |
4 |
@Name: PropEdit |
5 |
@Description: |
6 |
@@@: WikiPage Property Editor |
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: 2005/02/06 12:24:08 $ |
17 |
@RequiredPlugin[list]: |
18 |
WikiEdit |
19 |
WikiFormCore |
20 |
@Namespace: |
21 |
@@media-type: |
22 |
http://suika.fam.cx/~wakaba/-temp/2004/04/24/mt# |
23 |
@@pe: |
24 |
http://suika.fam.cx/~wakaba/archive/2004/7/20/sw-propedit# |
25 |
@@sw: |
26 |
http://suika.fam.cx/~wakaba/archive/2004/7/20/sw# |
27 |
|
28 |
PluginConst: |
29 |
@NS_XHTML1: |
30 |
http://www.w3.org/1999/xhtml |
31 |
|
32 |
ViewDefinition: |
33 |
@Mode: pe--propedit |
34 |
@Condition: |
35 |
@@http-method[list]: |
36 |
GET |
37 |
HEAD |
38 |
@Description: |
39 |
@@@: Editing WikiPage Properties |
40 |
@@lang: en |
41 |
@template: |
42 |
@@http-status-code: 200 |
43 |
@@media-type: text/html |
44 |
@@use-media-type-charset: 1 |
45 |
@@expires: %%edit%% |
46 |
@@body: |
47 |
%html-document ( |
48 |
title => {%res(name=>{PropEdit:WebPageTitle});}p, |
49 |
link-meta => {%template (name => links); |
50 |
%html-meta(name => ROBOTS, content => NOINDEX);}p, |
51 |
content => { |
52 |
%template ( |
53 |
name => ws--page, |
54 |
-content => { |
55 |
%section ( |
56 |
title => {%res (name => {PropEdit:Title});}p, heading, |
57 |
content => { |
58 |
%pe--propedit (template => { |
59 |
%resource-as-plain-text (name => {PropEdit:Template}); |
60 |
}p); |
61 |
}p, |
62 |
); |
63 |
}, |
64 |
); |
65 |
}p, |
66 |
); |
67 |
|
68 |
ViewDefinition: |
69 |
@Mode: -pe--conflict |
70 |
@Condition: |
71 |
@@http-method[list]: |
72 |
GET |
73 |
@Description: |
74 |
@@@: Editing WikiPage Properties -- Report Confliction |
75 |
@@lang: en |
76 |
@template: |
77 |
@@http-status-code: 409 |
78 |
@@media-type: text/html |
79 |
@@use-media-type-charset: 1 |
80 |
@@expires: %%error%% |
81 |
@@body: |
82 |
%html-document ( |
83 |
title => {%res(name=>{PropEdit:Conflict:WebPageTitle});}p, |
84 |
link-meta => {%template (name => links); |
85 |
%html-meta(name => ROBOTS, content => NOINDEX);}p, |
86 |
content => { |
87 |
%template ( |
88 |
name => ws--page, |
89 |
-content => { |
90 |
%section ( |
91 |
title => {%res (name => {PropEdit:Conflict:Title});}p, heading, |
92 |
content => { |
93 |
%paragraph (content => { |
94 |
%res (name => {PropEdit:Conflict:Description}); |
95 |
}p); |
96 |
%pe--propedit (template => { |
97 |
%resource-as-plain-text (name => {PropEdit:Template}); |
98 |
}p, |
99 |
); |
100 |
}p);}, |
101 |
); |
102 |
}p, |
103 |
); |
104 |
|
105 |
FormattingRule: |
106 |
@Category[list]: view |
107 |
@Name: pe--propedit |
108 |
@Description: |
109 |
@@@: A property editor form |
110 |
@@lang: en |
111 |
@Parameter: |
112 |
@@Name: page |
113 |
@@Type: WikiName |
114 |
@@Default: (auto) |
115 |
@@Description: |
116 |
@@@@: WikiPage to be edited |
117 |
@@@lang:en |
118 |
@Parameter: |
119 |
@@Name: template |
120 |
@@Type: template |
121 |
@@Default: (required) |
122 |
@@Description: |
123 |
@@@@: Template of the content |
124 |
@@@lang: en |
125 |
@Formatting: |
126 |
__ATTRTEXT:%page__;__ATTRTEXT:%template__; |
127 |
my $page = $o->{wiki}->name ($p->{page} || $o->{wiki}->{var}->{page}); |
128 |
my $template = $p->{template}; |
129 |
my $content = __FUNCPACK{WikiEdit}__->get_content ($o, $page); |
130 |
local $o->{var}->{content} = $content->{content}; |
131 |
local $o->{var}->{content_prop} = $content->{content_prop}; |
132 |
local $o->{var}->{source}->{prop} = sub { |
133 |
my %opt = @_; |
134 |
my $key = $opt{p}->{id}; |
135 |
my $uri = $opt{o}->{wiki}->{config}->{<Q:pe:prop>}->{$key}->{uri}; |
136 |
my $rv = {}; |
137 |
if ($uri) { |
138 |
if (my $nv = $opt{o}->{wiki}->{input}->parameter ('PE--CTRL--'.$key)) { |
139 |
$rv->{value} = $nv; |
140 |
$rv->{class} = [qw/pe--from-parameter/]; |
141 |
} else { |
142 |
$rv->{value} = $opt{o}->{var}->{content_prop} |
143 |
->get_attribute_value ($uri, |
144 |
default => $opt{p}->{default}); |
145 |
} |
146 |
} else { |
147 |
$rv->{value} = $opt{p}->{default}; |
148 |
} |
149 |
$rv; |
150 |
}; |
151 |
__FUNCPACK{WikiFormCore}__->make_form_in_html |
152 |
($p->{-parent}, $template, |
153 |
wiki => $o->{wiki}, |
154 |
o => $o, |
155 |
index => -1, |
156 |
local_id_prefix => 'PE--CTRL--', |
157 |
output => { |
158 |
mode => 'pe--write', |
159 |
page => $page, |
160 |
submit_button => 1, |
161 |
hidden => sub { |
162 |
my ($hidden, $o) = @_; |
163 |
## Embed hidden sequencial number to detect confliction |
164 |
for ($hidden->append_new_node (type => '#element', |
165 |
namespace_uri => $NS_XHTML1, |
166 |
local_name => 'input')) { |
167 |
$_->set_attribute (type => 'hidden'); |
168 |
$_->set_attribute (name => 'pe--seq'); |
169 |
$_->set_attribute (value => $o->{var}->{content_prop} |
170 |
->get_attribute_value (<Q:pe:seq>, |
171 |
default => 0)); |
172 |
$_->option (use_EmptyElemTag => 1); |
173 |
} |
174 |
}, |
175 |
}); |
176 |
|
177 |
ViewFragment: |
178 |
@Name: links |
179 |
@Description: |
180 |
@@@: Link to property edit mode of the WikiPage |
181 |
@@lang:en |
182 |
@Formatting: |
183 |
%link-wiki(mode => pe--propedit, up-to-date, |
184 |
rel => edit, class => wiki-cmd, |
185 |
description => {%res(name=>{PropEdit:Link:Description});}p); |
186 |
|
187 |
ViewDefinition: |
188 |
@Mode: pe--write |
189 |
@Condition: |
190 |
@@http-method[list]: |
191 |
POST |
192 |
@Description: |
193 |
@@@: Saving modified (new) WikiPage properties |
194 |
@@lang: en |
195 |
@Use: |
196 |
use Message::Util::Error; |
197 |
@method: |
198 |
@@Name: main |
199 |
@@@: |
200 |
my $wiki = $self->{view}->{wiki}; |
201 |
$wiki->{var}->{db}->{read_only}->{'content'} = 1; |
202 |
$wiki->{var}->{db}->{read_only}->{'content_prop'} = 0; |
203 |
$wiki->{var}->{db}->{read_only}->{'lastmodified'} = 1; |
204 |
$wiki->{var}->{db}->{read_only}->{'referer'} = 0; |
205 |
$wiki->init_db; |
206 |
|
207 |
my $page = $wiki->{var}->{page}; |
208 |
my $old_content = $wiki->{db}->get (content => $page); |
209 |
my $prop = $wiki->{db}->get (content_prop => $page); |
210 |
|
211 |
## Check confliction |
212 |
my $seq = $wiki->{input}->parameter ('pe--seq') || 0; |
213 |
if ($seq != $prop->get_attribute_value (<Q:pe:seq>, default => 0)) { |
214 |
$self->{view}->{wiki}->view_in_mode (mode => '-pe--conflict'); |
215 |
throw SuikaWiki::DB::Util::Error -type => 'ERROR_REPORTED'; |
216 |
} |
217 |
|
218 |
my %pprop; |
219 |
## NOTE: Uniqueness of stringified WikiName assumed. |
220 |
$pprop{my $page_name = $page->stringify (wiki => $wiki)} |
221 |
= {prop => $prop, page => $page}; |
222 |
my $this_page_name = $page_name; |
223 |
|
224 |
MODIFICATION: { |
225 |
|
226 |
my $old_mt; |
227 |
my $new_mt; |
228 |
my %depend; |
229 |
KEY: for my $key (grep s/^PE--CTRL--//, $wiki->{input}->parameter_names) { |
230 |
my $propinfo = $wiki->{config}->{<Q:pe:prop>}->{$key}; |
231 |
next KEY unless $propinfo->{uri}; |
232 |
|
233 |
## Really modified? |
234 |
my $new_value = [$wiki->{input}->parameter ('PE--CTRL--'.$key)]; |
235 |
if (@$new_value == 1) { |
236 |
if ($propinfo->{is_list}) { |
237 |
$new_value = [split /\x0A|\x0D\x0A?/, $new_value->[0]]; |
238 |
} |
239 |
if ($propinfo->{type} eq <Q:media-type:media-type>) { |
240 |
next KEY if $new_value->[0] eq '#asis'; |
241 |
} |
242 |
} |
243 |
|
244 |
my $old_value = $prop->get_attribute_value ($propinfo->{uri}); |
245 |
if (ref $old_value and @$new_value == @$old_value) { |
246 |
CHK: { |
247 |
for my $i (0..$#$new_value) { |
248 |
last CHK if $old_value->[$i] ne $new_value->[$i]; |
249 |
} |
250 |
next KEY; |
251 |
} |
252 |
} elsif (not ref $old_value and @$new_value == 1) { |
253 |
next KEY if $old_value eq $new_value->[0]; |
254 |
} |
255 |
if ($propinfo->{uri} eq <Q:media-type:media-type>) { |
256 |
$old_mt = $old_value; |
257 |
$new_mt = $new_value->[0]; |
258 |
} |
259 |
|
260 |
## Update to new value |
261 |
if (@$new_value) { |
262 |
if ($propinfo->{is_list}) { |
263 |
$prop->set_attribute ($propinfo->{uri} => $new_value); |
264 |
} elsif (length $new_value->[0]) { |
265 |
$prop->set_attribute ($propinfo->{uri} => $new_value->[0]); |
266 |
} else { |
267 |
$prop->remove_attribute ($propinfo->{uri}); |
268 |
} |
269 |
|
270 |
## Update other page's properties |
271 |
if ($propinfo->{type} eq <Q:sw:WikiName> and |
272 |
defined $propinfo->{<Q:pe:revWNList>}) { |
273 |
PV: for my $wn (@$new_value) { |
274 |
my $wiki_name = $wiki->name ($wn); |
275 |
unless ($pprop{$wn}->{prop}) { |
276 |
$pprop{$wn}->{prop} |
277 |
= $wiki->{db}->get (content_prop => $wiki_name); |
278 |
$pprop{$wn}->{page} = $wiki_name; |
279 |
} |
280 |
my $pv = $pprop{$wn}->{prop}->get_attribute_value |
281 |
($propinfo->{<Q:pe:revWNList>}, |
282 |
as_array => 1); |
283 |
for (@$pv) { |
284 |
next PV if $_ eq $this_page_name; |
285 |
} |
286 |
push @$pv, $this_page_name; |
287 |
$pprop{$wn}->{prop}->set_attribute |
288 |
($propinfo->{<Q:pe:revWNList>} => $pv); |
289 |
$pprop{$wn}->{modified} = 1; |
290 |
} |
291 |
} |
292 |
} else { |
293 |
$prop->remove_attribute ($propinfo->{uri}); |
294 |
} |
295 |
$pprop{$page_name}->{modified} = 1; |
296 |
$depend{$key} = $propinfo->{depend} if $propinfo->{depend}; |
297 |
} |
298 |
DEPEND: for my $sub (keys %depend) { |
299 |
for my $main (@{$depend{$sub}}) { |
300 |
unless ($prop->get_attribute ($wiki->{config}->{<Q:pe:prop>} |
301 |
->{$main}->{uri})) { |
302 |
$prop->remove_attribute ($wiki->{config}->{<Q:pe:prop>} |
303 |
->{$sub}->{uri}); |
304 |
next DEPEND; |
305 |
} |
306 |
} |
307 |
} |
308 |
last MODIFICATION unless $pprop{$page_name}->{modified}; |
309 |
|
310 |
## Update property |
311 |
for my $wn (keys %pprop) { |
312 |
next unless $pprop{$wn}->{modified}; |
313 |
my $prop = $pprop{$wn}->{prop}; |
314 |
|
315 |
my $seq = $prop->get_attribute_value (<Q:pe:seq>, default => 0); |
316 |
$prop->set_attribute (<Q:pe:seq> => $seq + 1); |
317 |
|
318 |
try { |
319 |
$self->{view}->{wiki}->{db}->set |
320 |
(content_prop => $pprop{$wn}->{page} => $prop); |
321 |
} catch SuikaWiki::DB::Util::Error with { |
322 |
my $err = shift; |
323 |
if ($err->{-type} eq 'ERROR_REPORTED') { |
324 |
$err->throw; |
325 |
} else { |
326 |
$self->{view}->{wiki}->view_in_mode (mode => '-wdb--fatal-error'); |
327 |
throw SuikaWiki::DB::Util::Error -type => 'ERROR_REPORTED'; |
328 |
} |
329 |
}; |
330 |
} |
331 |
|
332 |
## Post-write events |
333 |
if ($new_mt) { |
334 |
my $format = __FUNCPACK{WikiFormat}__ |
335 |
->handler (\$old_content, |
336 |
serialized_media_type => $old_mt, |
337 |
content_prop => $prop, |
338 |
wiki => $wiki); |
339 |
$format->content_type_changed_from (wiki => $wiki, |
340 |
page => $page, |
341 |
old_content => \$old_content, |
342 |
new_content => \$old_content, |
343 |
new_serialized_media_type => $new_mt, |
344 |
content_prop => $prop); |
345 |
} |
346 |
my $format = __FUNCPACK{WikiFormat}__ |
347 |
->handler (\$old_content, |
348 |
serialized_media_type => $new_mt, |
349 |
content_prop => $prop, |
350 |
wiki => $wiki); |
351 |
$format->content_prop_modified (wiki => $wiki, |
352 |
page => $page, |
353 |
content_prop => $prop); |
354 |
## ISSUE: Should for other modified WikiPages also fired? |
355 |
|
356 |
} # MODIFICATION |
357 |
|
358 |
## Redirect appropriate mode |
359 |
my $new_mode = $self->{view}->{wiki}->{input}->parameter |
360 |
('we--mode-modified'); |
361 |
$new_mode =~ s/[^0-9A-Za-z._-]+//g; |
362 |
my $uri = $self->{view}->{wiki}->uri_reference |
363 |
(page => $page, |
364 |
mode => $new_mode, |
365 |
up_to_date => 1, |
366 |
param => {we__mode_modified => $new_mode}); |
367 |
|
368 |
require SuikaWiki::Output::HTTP; |
369 |
my $output = SuikaWiki::Output::HTTP->new |
370 |
(wiki => $self->{view}->{wiki}); |
371 |
$output->set_redirect (uri => $uri, status_code => 303); |
372 |
$output->output (output => 'http-cgi'); |
373 |
|
374 |
FormattingRule: |
375 |
@Category[list]: |
376 |
form-input |
377 |
@Name: pe--texts |
378 |
@Description: |
379 |
@@@@: Multiple texts input |
380 |
@@@lang:en |
381 |
@Formatting: |
382 |
__ATTRTEXT:%id__;__ATTRNODE:%label__; |
383 |
my $id = __FUNCPACK{WikiFormCore}__ |
384 |
->control_id ($o, |
385 |
local_id => $p->{id}, |
386 |
require_local_id => 1); |
387 |
my $has_label = 0; |
388 |
if ($p->{label}->count) { |
389 |
$has_label = 1; |
390 |
for ($p->{-parent}->append_new_node |
391 |
(type => '#element', |
392 |
namespace_uri => $NS_XHTML1, |
393 |
local_name => 'label')) { |
394 |
$_->set_attribute (for => $id->{global_id}); |
395 |
$_->append_node ($p->{label}); |
396 |
} |
397 |
$p->{-parent}->append_new_node |
398 |
(type => '#element', |
399 |
namespace_uri => $NS_XHTML1, |
400 |
local_name => 'br') |
401 |
->option (use_EmptyElemTag => 1); |
402 |
} |
403 |
|
404 |
for ($p->{-parent}->append_new_node (type => '#element', |
405 |
namespace_uri => $NS_XHTML1, |
406 |
local_name => 'textarea')) { |
407 |
$_->set_attribute (name => $id->{local_id}); |
408 |
$_->set_attribute (id => $id->{global_id}) if $has_label; |
409 |
__ATTRTEXT:%size__;__ATTRTEXT:%lines__;__ATTRTEXT:%description__; |
410 |
$_->set_attribute (cols => ($p->{size}||20)*2); ## Length |
411 |
$_->set_attribute (title => $p->{description}) if $p->{description}; |
412 |
$_->set_attribute (disabled => 'disabled') if $o->{form}->{disabled}; |
413 |
__ATTRTEXT:%class__; |
414 |
my @class = split /\s+/, $p->{class}; |
415 |
push @class, 'require' if $o->{form}->{require}->{id}->{$p->{id}}; |
416 |
__ATTRTEXT:%source__; |
417 |
my $items = 5; |
418 |
if ($p->{source}) { |
419 |
if (ref $o->{var}->{source}->{$p->{source}}) { |
420 |
my $d = $o->{var}->{source}->{$p->{source}} |
421 |
->(p=>$p, o=>$o, type => <Q:pe:texts>); |
422 |
$_->append_text (ref $d->{value} eq 'ARRAY' ? |
423 |
join "\n", @{$d->{value}} : $d->{value}); |
424 |
push @class, @{$d->{class}||[]}; |
425 |
$items = @{$d->{value}} + 1 if ref $d->{value} eq 'ARRAY'; |
426 |
} else { |
427 |
$_->append_text (ref $o->{var}->{$p->{source}} eq 'ARRAY' ? |
428 |
join "\n", @{$o->{var}->{$p->{source}}} : |
429 |
$o->{var}->{$p->{source}}); |
430 |
$items = @{$o->{var}->{$p->{source}}} + 1 |
431 |
if ref $o->{var}->{$p->{source}} eq 'ARRAY'; |
432 |
} |
433 |
} else { |
434 |
__ATTRTEXT:%default__; |
435 |
$_->append_text ($p->{default}); ## Default value |
436 |
} |
437 |
$_->set_attribute (rows => ($p->{lines} || $items)); ## Lines |
438 |
$_->set_attribute (class => join ' ', @class) if @class; |
439 |
} |
440 |
|
441 |
Resource: |
442 |
@Edit:SaveAnd:pe--propedit:Description: |
443 |
@@lang: en |
444 |
@@@: |
445 |
Save modified property values and show this property-editing |
446 |
mode again. |
447 |
@Edit:SaveAnd:pe--propedit:Label: |
448 |
@@lang: en |
449 |
@@@: |
450 |
Editing Properties |
451 |
@PropEdit:Conflict:Description: |
452 |
@@lang: en |
453 |
@@@: |
454 |
Properties for WikiPage %link-to-wikipage (label => {%link-to-it ( |
455 |
label => {%page-name;}p);}); |
456 |
has been modified during you are editing them. |
457 |
Please ensure that third-party's modification does not |
458 |
affect your submitting changes. |
459 |
@PropEdit:Conflict:Title: |
460 |
@@lang: en |
461 |
@@@: |
462 |
Confliction - Editing Content Properties |
463 |
@PropEdit:Conflict:WebPageTitle: |
464 |
@@lang: en |
465 |
@@@: |
466 |
CONFLICTION - %page-name; (Editing Content Properties) |
467 |
@PropEdit:Link:Description: |
468 |
@@lang: en |
469 |
@@@: Edit properties of this WikiPage |
470 |
@PropEdit:Template: |
471 |
|
472 |
## Should be defined in resource WikiPage |
473 |
@PropEdit:Title: |
474 |
@@lang: en |
475 |
@@@: |
476 |
Editing Content Properties |
477 |
@PropEdit:WebPageTitle: |
478 |
@@lang: en |
479 |
@@@: |
480 |
%page-name; (Editing Content Properties) |
481 |
|
482 |
|
483 |
|