Filename | /var/www/foswiki11/lib/Foswiki/Plugins/MultiTopicSavePlugin.pm |
Statements | Executed 28 statements in 2.58ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 58µs | 223µs | initPlugin | Foswiki::Plugins::MultiTopicSavePlugin::
1 | 1 | 1 | 16µs | 26µs | BEGIN@31 | Foswiki::Plugins::MultiTopicSavePlugin::
1 | 1 | 1 | 16µs | 55µs | BEGIN@26 | Foswiki::Plugins::MultiTopicSavePlugin::
1 | 1 | 1 | 14µs | 27µs | BEGIN@22 | Foswiki::Plugins::MultiTopicSavePlugin::
1 | 1 | 1 | 13µs | 1.94ms | BEGIN@28 | Foswiki::Plugins::MultiTopicSavePlugin::
1 | 1 | 1 | 9µs | 26µs | BEGIN@33 | Foswiki::Plugins::MultiTopicSavePlugin::
1 | 1 | 1 | 4µs | 4µs | BEGIN@24 | Foswiki::Plugins::MultiTopicSavePlugin::
1 | 1 | 1 | 4µs | 4µs | BEGIN@25 | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | _MULTITOPICSAVEENDFORM | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | _MULTITOPICSAVEINPUT | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | _MULTITOPICSAVEMESSAGE | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | _MULTITOPICSAVESTARTFORM | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | _MULTITOPICSAVESUBMIT | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | _encodeHTMLEntities | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | _encodeValue | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | _fetchFormFieldValue | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | _topicLock | Foswiki::Plugins::MultiTopicSavePlugin::
0 | 0 | 0 | 0s | 0s | restMultiTopicSave | Foswiki::Plugins::MultiTopicSavePlugin::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for default license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package MultiTopicSavePlugin | ||||
6 | |||||
7 | This plugin enables saving content incl form data to multiple topics using | ||||
8 | only a single submit. | ||||
9 | |||||
10 | It is typically used where you list content of a number of topics in a formatted | ||||
11 | search which present the content as an HTML form with fields for each topic. | ||||
12 | |||||
13 | A submit button makes a rest call that causes this plugin to save the changed | ||||
14 | date to all the topics listed. | ||||
15 | |||||
16 | =cut | ||||
17 | |||||
18 | # change the package name!!! | ||||
19 | package Foswiki::Plugins::MultiTopicSavePlugin; | ||||
20 | |||||
21 | # Always use strict to enforce variable scoping | ||||
22 | 2 | 32µs | 2 | 39µs | # spent 27µs (14+13) within Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@22 which was called:
# once (14µs+13µs) by Foswiki::Plugin::BEGIN@2.23 at line 22 # spent 27µs making 1 call to Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@22
# spent 13µs making 1 call to strict::import |
23 | |||||
24 | 2 | 20µs | 1 | 4µs | # spent 4µs within Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@24 which was called:
# once (4µs+0s) by Foswiki::Plugin::BEGIN@2.23 at line 24 # spent 4µs making 1 call to Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@24 |
25 | 2 | 22µs | 1 | 4µs | # spent 4µs within Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@25 which was called:
# once (4µs+0s) by Foswiki::Plugin::BEGIN@2.23 at line 25 # spent 4µs making 1 call to Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@25 |
26 | 2 | 56µs | 2 | 93µs | # spent 55µs (16+39) within Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@26 which was called:
# once (16µs+39µs) by Foswiki::Plugin::BEGIN@2.23 at line 26 # spent 55µs making 1 call to Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@26
# spent 39µs making 1 call to CGI::import |
27 | |||||
28 | # spent 1.94ms (13µs+1.93) within Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@28 which was called:
# once (13µs+1.93ms) by Foswiki::Plugin::BEGIN@2.23 at line 35 | ||||
29 | # Backwards compatibility for Foswiki 1.1.x | ||||
30 | 1 | 10µs | 1 | 1.93ms | unless ( Foswiki::Request->can('multi_param') ) { # spent 1.93ms making 1 call to CGI::can |
31 | 2 | 41µs | 2 | 36µs | # spent 26µs (16+10) within Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@31 which was called:
# once (16µs+10µs) by Foswiki::Plugin::BEGIN@2.23 at line 31 # spent 26µs making 1 call to Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@31
# spent 10µs making 1 call to warnings::unimport |
32 | 1 | 2µs | *Foswiki::Request::multi_param = \&Foswiki::Request::param; | ||
33 | 2 | 25µs | 2 | 43µs | # spent 26µs (9+17) within Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@33 which was called:
# once (9µs+17µs) by Foswiki::Plugin::BEGIN@2.23 at line 33 # spent 26µs making 1 call to Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@33
# spent 17µs making 1 call to warnings::import |
34 | } | ||||
35 | 1 | 2.32ms | 1 | 1.94ms | } # spent 1.94ms making 1 call to Foswiki::Plugins::MultiTopicSavePlugin::BEGIN@28 |
36 | |||||
37 | # $VERSION should always be in the format$Rev: 9013 (2010-09-12) $ so that Foswiki can | ||||
38 | # determine the checked-in status of the extension. | ||||
39 | 1 | 700ns | our $VERSION = '1.10'; | ||
40 | |||||
41 | # $RELEASE is used in the "Find More Extensions" automation in configure. | ||||
42 | 1 | 300ns | our $RELEASE = '1.10'; | ||
43 | |||||
44 | # Short description of this plugin | ||||
45 | # One line description, is shown in the %SYSTEMWEB%.TextFormattingRules topic: | ||||
46 | 1 | 200ns | our $SHORTDESCRIPTION = | ||
47 | 'Save form data to multiple topics in one single submission'; | ||||
48 | |||||
49 | # No preferences set in the plugin topic. | ||||
50 | 1 | 200ns | our $NO_PREFS_IN_TOPIC = 1; | ||
51 | |||||
52 | =begin TML | ||||
53 | |||||
54 | ---++ initPlugin($topic, $web, $user) -> $boolean | ||||
55 | * =$topic= - the name of the topic in the current CGI query | ||||
56 | * =$web= - the name of the web in the current CGI query | ||||
57 | * =$user= - the login name of the user | ||||
58 | * =$installWeb= - the name of the web the plugin topic is in | ||||
59 | (usually the same as =$Foswiki::cfg{SystemWebName}=) | ||||
60 | |||||
61 | Called to initialise the plugin. If everything is OK, should return | ||||
62 | a non-zero value. On non-fatal failure, should write a message | ||||
63 | using =Foswiki::Func::writeWarning= and return 0. In this case | ||||
64 | %<nop>FAILEDPLUGINS% will indicate which plugins failed. | ||||
65 | |||||
66 | In the case of a catastrophic failure that will prevent the whole | ||||
67 | installation from working safely, this handler may use 'die', which | ||||
68 | will be trapped and reported in the browser. | ||||
69 | |||||
70 | __Note:__ Please align macro names with the Plugin name, e.g. if | ||||
71 | your Plugin is called !FooBarPlugin, name macros FOOBAR and/or | ||||
72 | FOOBARSOMETHING. This avoids namespace issues. | ||||
73 | |||||
74 | =cut | ||||
75 | |||||
76 | # spent 223µs (58+165) within Foswiki::Plugins::MultiTopicSavePlugin::initPlugin which was called:
# once (58µs+165µs) by Foswiki::Plugin::__ANON__[/var/www/foswiki11/lib/Foswiki/Plugin.pm:241] at line 234 of /var/www/foswiki11/lib/Foswiki/Plugin.pm | ||||
77 | 1 | 2µs | my ( $topic, $web, $user, $installWeb ) = @_; | ||
78 | |||||
79 | # check for Plugins.pm versions | ||||
80 | 1 | 16µs | 1 | 9µs | if ( $Foswiki::Plugins::VERSION < 2.0 ) { # spent 9µs making 1 call to version::vxs::VCMP |
81 | Foswiki::Func::writeWarning( 'Version mismatch between ', | ||||
82 | __PACKAGE__, ' and Plugins.pm' ); | ||||
83 | return 0; | ||||
84 | } | ||||
85 | |||||
86 | # Example code of how to get a preference value, register a macro | ||||
87 | # handler and register a RESTHandler (remove code you do not need) | ||||
88 | |||||
89 | # Set your per-installation plugin configuration in LocalSite.cfg, | ||||
90 | # like this: | ||||
91 | # $Foswiki::cfg{Plugins}{EmptyPlugin}{ExampleSetting} = 1; | ||||
92 | # See %SYSTEMWEB%.DevelopingPlugins#ConfigSpec for information | ||||
93 | # on integrating your plugin configuration with =configure=. | ||||
94 | |||||
95 | # Always provide a default in case the setting is not defined in | ||||
96 | # LocalSite.cfg. See %SYSTEMWEB%.Plugins for help in adding your plugin | ||||
97 | # configuration to the =configure= interface. | ||||
98 | # my $setting = $Foswiki::cfg{Plugins}{EmptyPlugin}{ExampleSetting} || 0; | ||||
99 | |||||
100 | # Register the _EXAMPLETAG function to handle %EXAMPLETAG{...}% | ||||
101 | # This will be called whenever %EXAMPLETAG% or %EXAMPLETAG{...}% is | ||||
102 | # seen in the topic text. | ||||
103 | 1 | 4µs | 1 | 27µs | Foswiki::Func::registerTagHandler( 'MULTITOPICSAVESUBMIT', # spent 27µs making 1 call to Foswiki::Func::registerTagHandler |
104 | \&_MULTITOPICSAVESUBMIT | ||||
105 | ); | ||||
106 | |||||
107 | 1 | 2µs | 1 | 21µs | Foswiki::Func::registerTagHandler( 'MULTITOPICSAVEINPUT', # spent 21µs making 1 call to Foswiki::Func::registerTagHandler |
108 | \&_MULTITOPICSAVEINPUT | ||||
109 | ); | ||||
110 | 1 | 4µs | 1 | 21µs | Foswiki::Func::registerTagHandler( 'MULTITOPICSAVEMESSAGE', # spent 21µs making 1 call to Foswiki::Func::registerTagHandler |
111 | \&_MULTITOPICSAVEMESSAGE | ||||
112 | ); | ||||
113 | |||||
114 | 1 | 3µs | 1 | 21µs | Foswiki::Func::registerTagHandler( 'MULTITOPICSAVESTARTFORM', # spent 21µs making 1 call to Foswiki::Func::registerTagHandler |
115 | \&_MULTITOPICSAVESTARTFORM | ||||
116 | ); | ||||
117 | |||||
118 | 1 | 3µs | 1 | 34µs | Foswiki::Func::registerTagHandler( 'MULTITOPICSAVEENDFORM', # spent 34µs making 1 call to Foswiki::Func::registerTagHandler |
119 | \&_MULTITOPICSAVEENDFORM | ||||
120 | ); | ||||
121 | |||||
122 | # Allow a sub to be called from the REST interface | ||||
123 | # using the provided alias | ||||
124 | 1 | 5µs | 1 | 32µs | Foswiki::Func::registerRESTHandler( 'multitopicsave', # spent 32µs making 1 call to Foswiki::Func::registerRESTHandler |
125 | \&restMultiTopicSave, | ||||
126 | authenticate => 1, | ||||
127 | http_allow => 'POST', | ||||
128 | validate => 0 | ||||
129 | ); | ||||
130 | |||||
131 | # Plugin correctly initialized | ||||
132 | 1 | 7µs | return 1; | ||
133 | } | ||||
134 | |||||
135 | =begin TML | ||||
136 | |||||
137 | ---++ _topicLock($web, $topic, $mode) -> ($success, $message) | ||||
138 | |||||
139 | This is the which will lock or unlock a topic | ||||
140 | |||||
141 | The parameter is: | ||||
142 | * =$session= - The Foswiki object associated to this session. | ||||
143 | * =$mode= - "locked" or "released", default "released" | ||||
144 | |||||
145 | The sub returns | ||||
146 | * =$success= - 0 = failed, 1 = success | ||||
147 | * =$message= - a message how it went. | ||||
148 | |||||
149 | Note: Message from this sub is not yet used in the plugin. Nice to have | ||||
150 | |||||
151 | =cut | ||||
152 | |||||
153 | sub _topicLock { | ||||
154 | my ($web, $topic, $mode) = @_; | ||||
155 | |||||
156 | $mode ||= "released"; | ||||
157 | |||||
158 | my $currentWikiName = Foswiki::Func::getWikiName( ); | ||||
159 | |||||
160 | my $message = ''; | ||||
161 | |||||
162 | unless ( Foswiki::Func::topicExists( $web, $topic ) ) { | ||||
163 | $message .= "Topic $web.$topic does not exist\n\n"; | ||||
164 | return ( 0, $message ); | ||||
165 | } | ||||
166 | |||||
167 | # Is it locked? | ||||
168 | my ($oopsUrl, $loginName, $unlockTime) = | ||||
169 | Foswiki::Func::checkTopicEditLock( $web, $topic, undef ); | ||||
170 | |||||
171 | my $lockedWikiName = Foswiki::Func::getWikiName( $loginName ); | ||||
172 | |||||
173 | # We cannot lock or unlock if locked by someone else | ||||
174 | if ( $unlockTime && ( $lockedWikiName ne $currentWikiName ) ) { | ||||
175 | $message .= "Topic $web.$topic was not $mode. " . | ||||
176 | "It is being edited by !$lockedWikiName"; | ||||
177 | |||||
178 | return ( 0, $message ); | ||||
179 | } | ||||
180 | |||||
181 | # We cannot lock or unlock unless we have access rights | ||||
182 | unless ( Foswiki::Func::checkAccessPermission( 'CHANGE', $currentWikiName, | ||||
183 | undef, $topic, $web ) ) { | ||||
184 | |||||
185 | $message .= "Topic $web.$topic was not $mode " . | ||||
186 | "due to lack of access rights\n\n"; | ||||
187 | |||||
188 | return ( 0, $message ); | ||||
189 | } | ||||
190 | |||||
191 | # Success, we can lock or release | ||||
192 | Foswiki::Func::setTopicEditLock( $web, $topic, ($mode eq 'locked') ); | ||||
193 | |||||
194 | $message .= "Topic $web.$topic was $mode."; | ||||
195 | |||||
196 | return ( 1, $message ); | ||||
197 | } | ||||
198 | |||||
199 | |||||
200 | =begin TML | ||||
201 | |||||
202 | ---++ _fetchFormFieldValue($field, $web, $topic) -> $value | ||||
203 | |||||
204 | Fetch the raw unrendered content of a formfield | ||||
205 | |||||
206 | The parameter is: | ||||
207 | * =$field= - Field name to fetch. | ||||
208 | * =$web= - Web name of topic | ||||
209 | * =$topic= - Topic name | ||||
210 | |||||
211 | The sub returns | ||||
212 | * String - The raw content in the field | ||||
213 | * Returns '' if topic does not exist or no access rights | ||||
214 | |||||
215 | =cut | ||||
216 | |||||
217 | sub _fetchFormFieldValue { | ||||
218 | my ($field, $web, $topic) = @_; | ||||
219 | |||||
220 | my $currentWikiName = Foswiki::Func::getWikiName( ); | ||||
221 | |||||
222 | unless ( Foswiki::Func::checkAccessPermission( 'VIEW', $currentWikiName, | ||||
223 | undef, $topic, $web ) ) { | ||||
224 | return ''; | ||||
225 | } | ||||
226 | |||||
227 | my ( $meta, undef ) = Foswiki::Func::readTopic( $web, $topic ); | ||||
228 | if ( $field eq 'text' ) { return $meta->text; } | ||||
229 | my $value = $meta->get( 'FIELD', $field ); | ||||
230 | |||||
231 | my $returnvalue = defined $value ? $value->{'value'} : ''; | ||||
232 | |||||
233 | return $returnvalue; | ||||
234 | } | ||||
235 | |||||
236 | =begin TML | ||||
237 | |||||
238 | ---++ _encodeValue($value) -> $value | ||||
239 | |||||
240 | This function returns the input string encoded for view in TML tables | ||||
241 | We encode the most common destroyers of TML tables: | ||||
242 | newlines and vertical bars | ||||
243 | |||||
244 | =cut | ||||
245 | |||||
246 | sub _encodeValue { | ||||
247 | my ( $value ) = @_; | ||||
248 | $value =~ s/\r?\n/<br \/>/gs; | ||||
249 | my $bar = '|'; | ||||
250 | $value =~ s/\|/$bar/g; | ||||
251 | return $value | ||||
252 | } | ||||
253 | |||||
254 | =begin TML | ||||
255 | |||||
256 | ---++ _encodeHTMLEntities($value) -> $value | ||||
257 | |||||
258 | This function encodes special characters to html entities | ||||
259 | so values can be used in input fields in edit mode | ||||
260 | |||||
261 | =cut | ||||
262 | |||||
263 | sub _encodeHTMLEntities { | ||||
264 | my ( $value ) = @_; | ||||
265 | $value =~ s/'/'/g; | ||||
266 | $value =~ s/</</g; | ||||
267 | $value =~ s/>/>/g; | ||||
268 | return $value; | ||||
269 | } | ||||
270 | |||||
271 | # The function used to handle the %MULTITOPICSAVESUBMIT{...}% macro | ||||
272 | sub _MULTITOPICSAVESUBMIT { | ||||
273 | my($session, $params, $theTopic, $theWeb) = @_; | ||||
274 | # $session - a reference to the Foswiki session object (if you don't know | ||||
275 | # what this is, just ignore it) | ||||
276 | # $params= - a reference to a Foswiki::Attrs object containing | ||||
277 | # parameters. | ||||
278 | # This can be used as a simple hash that maps parameter names | ||||
279 | # to values, with _DEFAULT being the name for the default | ||||
280 | # (unnamed) parameter. | ||||
281 | # returnweb = the web we want to return to after submit (optional) | ||||
282 | # returntopic = the topic we want to return to after submit | ||||
283 | # can be web.topic format | ||||
284 | # $theTopic - name of the topic in the query | ||||
285 | # $theWeb - name of the web in the query | ||||
286 | # Return: the result of processing the macro. This will replace the | ||||
287 | # macro call in the final text. | ||||
288 | |||||
289 | # For example, %EXAMPLETAG{'hamburger' sideorder="onions"}% | ||||
290 | # $params->{_DEFAULT} will be 'hamburger' | ||||
291 | # $params->{sideorder} will be 'onions' | ||||
292 | |||||
293 | # We cannot use the rest parameter endPoint because we need to return | ||||
294 | # to the submit with the URLPARAM MULTITOPICSAVEMESSAGE set. | ||||
295 | |||||
296 | my $web = defined $params->{returnweb} ? $params->{returnweb} : $theWeb; | ||||
297 | my $topic = defined $params->{returntopic} ? $params->{returntopic} : $theTopic; | ||||
298 | # If returnweb was undefined and returntopic is web.topic form the result | ||||
299 | # is as expected web.topic. | ||||
300 | ($web, $topic) = Foswiki::Func::normalizeWebTopicName($web, $topic); | ||||
301 | |||||
302 | my $result = "<input type='hidden' name='redirectweb' value='$web' />" . | ||||
303 | "<input type='hidden' name='redirecttopic' value='$topic' />" . | ||||
304 | "<input type='hidden' name='topic' value='$web.$topic' />" . | ||||
305 | "<input type='submit' class='foswikiButton' value='"; | ||||
306 | $result .= $params->{_DEFAULT} || "Submit All Changes"; | ||||
307 | $result .= "' />"; | ||||
308 | |||||
309 | return $result; | ||||
310 | |||||
311 | } | ||||
312 | |||||
313 | # The function used to handle the %MULTITOPICSAVEINPUT{...}% macro | ||||
314 | sub _MULTITOPICSAVEINPUT { | ||||
315 | my($session, $params, $theTopic, $theWeb) = @_; | ||||
316 | # $session - a reference to the Foswiki session object (if you don't know | ||||
317 | # what this is, just ignore it) | ||||
318 | # $params= - a reference to a Foswiki::Attrs object containing | ||||
319 | # parameters. | ||||
320 | # This can be used as a simple hash that maps parameter names | ||||
321 | # to values, with _DEFAULT being the name for the default | ||||
322 | # (unnamed) parameter. | ||||
323 | # $theTopic - name of the topic in the query | ||||
324 | # $theWeb - name of the web in the query | ||||
325 | # Return: the result of processing the macro. This will replace the | ||||
326 | # macro call in the final text. | ||||
327 | |||||
328 | # For example, %EXAMPLETAG{'hamburger' sideorder="onions"}% | ||||
329 | # $params->{_DEFAULT} will be 'hamburger' | ||||
330 | # INPUT will be 'onions' | ||||
331 | |||||
332 | my $result = ''; | ||||
333 | my $type = lc ( $params->{type} ) || 'text'; | ||||
334 | my $size = $params->{size}; # No default here | ||||
335 | my $multiple = $params->{multiple} || 0; | ||||
336 | my $field = $params->{_DEFAULT}; | ||||
337 | my $targetWeb = $params->{web} || $theWeb; | ||||
338 | my $targetTopic = $params->{topic} || $theTopic; | ||||
339 | my $currentWikiName = Foswiki::Func::getWikiName( ); | ||||
340 | my $placeholder = $params->{placeholder}; | ||||
341 | |||||
342 | ( $targetWeb, $targetTopic ) = | ||||
343 | Foswiki::Func::normalizeWebTopicName( $targetWeb, $targetTopic ); | ||||
344 | |||||
345 | my $topicFQN = "$targetWeb.$targetTopic"; # Topic fully qualified name | ||||
346 | |||||
347 | # Special field text is the topic text and must always be textarea | ||||
348 | $type = 'textarea' if ( $field eq 'text'); | ||||
349 | |||||
350 | # Delay means we escape with $percnt and $quot | ||||
351 | # We cannot replace inside _RAW because that also replace quotes in values | ||||
352 | my $delay = $params->{delay}; | ||||
353 | if ( $delay && ( $delay =~ /\d+/ ) && $delay-- > 0 ) { | ||||
354 | $result = "\$percntMULTITOPICSAVEINPUT{"; | ||||
355 | $result .= "\$quot$field\$quot "; | ||||
356 | |||||
357 | foreach my $key ( keys %$params ) { | ||||
358 | next if ($key eq '_RAW' || $key eq '_DEFAULT'); | ||||
359 | |||||
360 | if ( $key eq 'delay' ) { | ||||
361 | $result .= "delay=\$quot$delay\$quot "; | ||||
362 | next; | ||||
363 | } | ||||
364 | |||||
365 | $result .= "$key=\$quot" . $params->{$key} . "\$quot "; | ||||
366 | } | ||||
367 | $result .= "}\$percnt"; | ||||
368 | return $result | ||||
369 | } | ||||
370 | |||||
371 | # if value is not defined we set it to the string $value | ||||
372 | my $value = defined $params->{value} ? $params->{value} : '$value'; | ||||
373 | |||||
374 | # Substitute the string '$value' by $value. This enables the user to | ||||
375 | # Prefix and suffix the existing value | ||||
376 | $value =~ | ||||
377 | s/\$value/_fetchFormFieldValue( $field, $targetWeb, $targetTopic )/ge; | ||||
378 | |||||
379 | # If edit mode is defined and set to off - just return the value | ||||
380 | my $editmode = defined $params->{editmode} ? | ||||
381 | Foswiki::Func::isTrue( $params->{editmode} ) : 1; | ||||
382 | |||||
383 | my $lockmode = defined $params->{lockmode} ? | ||||
384 | Foswiki::Func::isTrue( $params->{lockmode} ) : 0; | ||||
385 | |||||
386 | # encodeview is designed to be used in TML tables and encode the most | ||||
387 | # destroyers of TML tables, newlines and vertical bars | ||||
388 | my $encodeview = defined $params->{encodeview} ? | ||||
389 | Foswiki::Func::isTrue( $params->{encodeview} ) : 1; | ||||
390 | |||||
391 | if ( $currentWikiName eq $Foswiki::cfg{DefaultUserWikiName} ) { | ||||
392 | $value = _encodeValue($value) if $encodeview; | ||||
393 | return $value; | ||||
394 | } | ||||
395 | |||||
396 | if ( $editmode ) { | ||||
397 | # if lockmode is enabled we lock topic when in edit mode and | ||||
398 | # return the value if the lock failed since we cannot edit | ||||
399 | # Otherwise input fields need special characters html encoded | ||||
400 | if ( $lockmode ) { | ||||
401 | unless ( (_topicLock( $targetWeb, $targetTopic, 'locked' ))[0] ) { | ||||
402 | $value = _encodeValue($value) if $encodeview; | ||||
403 | return $value; | ||||
404 | } | ||||
405 | } | ||||
406 | $value = _encodeHTMLEntities( $value ); | ||||
407 | } | ||||
408 | else { | ||||
409 | # if lockmode is enabled we release topic lease when in non-edit mode | ||||
410 | _topicLock( $targetWeb, $targetTopic, 'released' ) | ||||
411 | if $lockmode; | ||||
412 | return '' if ( $type eq 'hidden' ); | ||||
413 | $value = _encodeValue($value) if $encodeview; | ||||
414 | return $value; | ||||
415 | } | ||||
416 | |||||
417 | # We assume leading and trailing spaces around option values are unwanted | ||||
418 | my @options = (); | ||||
419 | if ( defined $params->{options} ) { | ||||
420 | @options = map { s/^\s*(.*?)\s*$/$1/; $_; } | ||||
421 | split( /\s*,\s*/, $params->{options} ); | ||||
422 | } | ||||
423 | |||||
424 | # Render the field for editing depending on field type | ||||
425 | if ( $type eq 'text' ) { | ||||
426 | $result = "<input class='foswikiInputField' type='text' "; | ||||
427 | $size = 10 if ( !$size || $size < 1 ); | ||||
428 | $result .= "size='$size' "; | ||||
429 | $result .= "name='multitopicsavefield{$topicFQN}{$field}' "; | ||||
430 | $result .= "placeholder='$placeholder' " if defined $placeholder; | ||||
431 | $result .= "value='" . $value . "' />"; | ||||
432 | } | ||||
433 | elsif ( $type eq 'textarea' ) { | ||||
434 | $result = "<textarea class='foswikiTextarea' "; | ||||
435 | my ($cols, $rows) = ( 40, 5 ); | ||||
436 | if ( defined $size ) { | ||||
437 | if ( $size =~ m/(\d+)[xX](\d+)/ ) { | ||||
438 | ($cols, $rows) = ( $1, $2 ); | ||||
439 | } | ||||
440 | } | ||||
441 | $result .= "cols='$cols' rows='$rows' "; | ||||
442 | $result .= "name='multitopicsavefield{$topicFQN}{$field}'>"; | ||||
443 | $result .= "$value"; | ||||
444 | $result .= "</textarea>" | ||||
445 | } | ||||
446 | elsif ( $type eq 'radio' || $type eq 'checkbox' ) { | ||||
447 | my @values | ||||
448 | = map { s/^\s*(.*?)\s*$/$1/; $_; } split( /\s*,\s*/, $value ); | ||||
449 | my $initcounter = defined $size ? $size : 1; | ||||
450 | my $counter = $initcounter; | ||||
451 | $result = "<table style='border:none;border-style:none;border-width:0;padding-top:0;'>"; | ||||
452 | foreach my $option ( @options ) { | ||||
453 | $result .= "<tr>" if ( $counter == $initcounter ); | ||||
454 | $result .= "<td style='border:none;border-style:none;border-width:0;padding-top:0;'><input type='$type' "; | ||||
455 | $result .= "name='multitopicsavefield{$topicFQN}{$field}' "; | ||||
456 | $result .= "value='" . $option . "' "; | ||||
457 | if ( $type eq 'radio' && $option eq $value ) { | ||||
458 | $result .= "checked='checked' "; | ||||
459 | } | ||||
460 | if ( $type eq 'checkbox' && grep (/^$option$/, @values ) ) { | ||||
461 | $result .= "checked='checked' "; | ||||
462 | } | ||||
463 | $result .= "/> $option </td>"; | ||||
464 | unless ( --$counter ) { | ||||
465 | $result .= "</tr> "; | ||||
466 | $counter = $initcounter; | ||||
467 | } | ||||
468 | } | ||||
469 | $result .= "</tr>" if ( $counter != $initcounter ); | ||||
470 | $result .= "</table>"; | ||||
471 | |||||
472 | # We need a dummy hidden field to be able to send | ||||
473 | # none of the checkboxes selected from the browser | ||||
474 | $result .= "<input type='hidden' name='multitopicsavefield{$topicFQN}{$field}' value='' />" | ||||
475 | if ($type eq 'checkbox'); | ||||
476 | } | ||||
477 | elsif ( $type eq 'select' ) { | ||||
478 | my @values = | ||||
479 | map { s/^\s*(.*?)\s*$/$1/; $_; } split( /\s*,\s*/, $value ); | ||||
480 | |||||
481 | $result = "<select " | ||||
482 | . "name='multitopicsavefield{$topicFQN}{$field}' " | ||||
483 | . "class='foswikiSelect' "; | ||||
484 | |||||
485 | $result .= "multiple='multiple' " | ||||
486 | if Foswiki::Func::isTrue( $multiple ); | ||||
487 | |||||
488 | $result .= "size='$size'" | ||||
489 | if defined $size; | ||||
490 | |||||
491 | $result .= ">"; | ||||
492 | |||||
493 | foreach my $option ( @options ) { | ||||
494 | $result .= "<option class='foswikiOption' "; | ||||
495 | |||||
496 | if ( grep (/^$option$/, @values ) ) { | ||||
497 | $result .= "selected='selected' "; | ||||
498 | } | ||||
499 | |||||
500 | $result .= ">" . $option . "</option>"; | ||||
501 | } | ||||
502 | |||||
503 | $result .= "</select>"; | ||||
504 | |||||
505 | # We need a dummy hidden field to be able to send | ||||
506 | # none of the checkboxes selected from the browser | ||||
507 | $result .= "<input type='hidden' name='multitopicsavefield{$topicFQN}{$field}' value='' />"; | ||||
508 | } | ||||
509 | elsif ( $type eq 'hidden' ) { | ||||
510 | $result = "<input type='hidden' "; | ||||
511 | $result .= "name='multitopicsavefield{$topicFQN}{$field}' "; | ||||
512 | $result .= "value='" . $value . "' />"; | ||||
513 | } | ||||
514 | # else if the type is unknown we return nothing | ||||
515 | |||||
516 | return $result; | ||||
517 | } | ||||
518 | |||||
519 | |||||
520 | # The function used to handle the %MULTITOPICSAVEMESSAGE% macro | ||||
521 | sub _MULTITOPICSAVEMESSAGE { | ||||
522 | my($session, $params, $theTopic, $theWeb) = @_; | ||||
523 | # $session - a reference to the Foswiki session object (if you don't know | ||||
524 | # what this is, just ignore it) | ||||
525 | # $params= - a reference to a Foswiki::Attrs object containing | ||||
526 | # parameters. | ||||
527 | # This can be used as a simple hash that maps parameter names | ||||
528 | # to values, with _DEFAULT being the name for the default | ||||
529 | # (unnamed) parameter. | ||||
530 | # $theTopic - name of the topic in the query | ||||
531 | # $theWeb - name of the web in the query | ||||
532 | # Return: the result of processing the macro. This will replace the | ||||
533 | # macro call in the final text. | ||||
534 | |||||
535 | # For example, %EXAMPLETAG{'hamburger' sideorder="onions"}% | ||||
536 | # $params->{_DEFAULT} will be 'hamburger' | ||||
537 | # $params->{sideorder} will be 'onions' | ||||
538 | |||||
539 | return "%URLPARAM{\"MULTITOPICSAVEMESSAGE\"}%"; | ||||
540 | } | ||||
541 | |||||
542 | |||||
543 | # The function used to handle the %MULTITOPICSAVESTARTFORM% macro | ||||
544 | sub _MULTITOPICSAVESTARTFORM { | ||||
545 | my($session, $params, $theTopic, $theWeb) = @_; | ||||
546 | |||||
547 | my $result = "<form action='%SCRIPTURL{\"rest\"}%/" . | ||||
548 | "MultiTopicSavePlugin/multitopicsave' method='post'>"; | ||||
549 | return $result; | ||||
550 | |||||
551 | } | ||||
552 | |||||
553 | |||||
554 | # The function used to handle the %MULTITOPICSAVEENDFORM% macro | ||||
555 | sub _MULTITOPICSAVEENDFORM { | ||||
556 | my($session, $params, $theTopic, $theWeb) = @_; | ||||
557 | |||||
558 | return "</form>"; | ||||
559 | } | ||||
560 | |||||
561 | |||||
562 | =begin TML | ||||
563 | |||||
564 | ---++ restMultiTopicSave($session) -> $text | ||||
565 | |||||
566 | This is the sub which is called called by the =rest= script with multitopicsave. | ||||
567 | |||||
568 | The parameter is: | ||||
569 | * =$session= - The Foswiki object associated to this session. | ||||
570 | |||||
571 | Additional parameters can be recovered via the query object in the $session, for example: | ||||
572 | |||||
573 | my $query = $session->{request}; | ||||
574 | my $web = $query->{param}->{web}[0]; | ||||
575 | |||||
576 | For more information, check %SYSTEMWEB%.CommandAndCGIScripts#rest | ||||
577 | |||||
578 | For information about handling error returns from REST handlers, see | ||||
579 | Foswiki::Support.Faq1 | ||||
580 | |||||
581 | *Since:* Foswiki::Plugins::VERSION 2.0 | ||||
582 | |||||
583 | =cut | ||||
584 | |||||
585 | sub restMultiTopicSave { | ||||
586 | my ($session) = @_; | ||||
587 | |||||
588 | my $query = Foswiki::Func::getCgiQuery(); | ||||
589 | |||||
590 | my $redirecttopic = $query->param('redirecttopic') || ''; | ||||
591 | my $redirectweb = $query->param('redirectweb') || ''; | ||||
592 | my $sessionweb = $session->{webName}; | ||||
593 | my $sessiontopic = $session->{topicName}; | ||||
594 | my %parameters = (); | ||||
595 | my $currentWikiName = Foswiki::Func::getWikiName( ); | ||||
596 | |||||
597 | ( $redirectweb, $redirecttopic ) = | ||||
598 | Foswiki::Func::normalizeWebTopicName( $redirectweb, $redirecttopic ); | ||||
599 | |||||
600 | my $url = Foswiki::Func::getScriptUrl( $redirectweb, $redirecttopic, 'view' ); | ||||
601 | my $message = ''; | ||||
602 | |||||
603 | if ( $currentWikiName eq $Foswiki::cfg{DefaultUserWikiName} ) { | ||||
604 | $message = "Only authenticated users are allowed to save multiple topics\n\n"; | ||||
605 | $query->param(-name => 'MULTITOPICSAVEMESSAGE', -value => "$message"); | ||||
606 | Foswiki::Func::redirectCgiQuery( undef, $url, 1 ); | ||||
607 | return undef; | ||||
608 | } | ||||
609 | |||||
610 | # First we put all the multitopicsavefield parameters in a hash | ||||
611 | # parameters{topicname}{field}=value where value can be an array of | ||||
612 | # values from select fields | ||||
613 | foreach my $key ( $query->param() ) { | ||||
614 | if ( $key =~ /^multitopicsavefield{(.*?)}{(.*?)}$/ ) { | ||||
615 | |||||
616 | my $topic = $1; | ||||
617 | my $fieldName = $2; | ||||
618 | my @fieldValues = $query->multi_param($key); | ||||
619 | |||||
620 | # Remove empty values, they are most likely dummy values used | ||||
621 | # to indicate that no value in a multiple checkbox or select | ||||
622 | # is selected | ||||
623 | @fieldValues = grep( /.+/, @fieldValues); | ||||
624 | $parameters{$topic}{$fieldName} = join(", ", @fieldValues ); | ||||
625 | } | ||||
626 | } | ||||
627 | |||||
628 | # Now we traverse each topic and save all the parameters for | ||||
629 | # each topic if they have changed. | ||||
630 | |||||
631 | my $topicsavecounter = 0; | ||||
632 | |||||
633 | foreach my $topickey ( keys %parameters ) { | ||||
634 | |||||
635 | my $saveThisTopic = 0; # if 1 access is checked and granted | ||||
636 | |||||
637 | my ( $web, $topic ) = | ||||
638 | Foswiki::Func::normalizeWebTopicName( '', $topickey ); | ||||
639 | |||||
640 | my ( $meta, $text ) = Foswiki::Func::readTopic( $web, $topic ); | ||||
641 | |||||
642 | foreach my $fieldName ( keys %{$parameters{$topickey}} ) { | ||||
643 | my $value = $parameters{$topickey}{$fieldName}; | ||||
644 | my $oldValue; | ||||
645 | |||||
646 | if ( $fieldName eq 'text' ) { | ||||
647 | $oldValue = $text; | ||||
648 | |||||
649 | # Remove carriage returns because the Store also does that | ||||
650 | # The normal storage of a topic always leaves at least one new line | ||||
651 | $value =~ s/\r//g; | ||||
652 | $value = "\n" if ( $value eq '' ); | ||||
653 | } | ||||
654 | else { | ||||
655 | my $fieldhashref = $meta->get( 'FIELD', $fieldName ); | ||||
656 | |||||
657 | $oldValue = $fieldhashref ? | ||||
658 | $meta->get( 'FIELD', $fieldName )->{'value'} : | ||||
659 | ''; | ||||
660 | } | ||||
661 | |||||
662 | # Note: We can actually find and store fields that are not in the | ||||
663 | # form definition topic. Let us call this a feature. Could be | ||||
664 | # useful for finding old formfields in topics after form is altered | ||||
665 | if ( $oldValue ne $value ) { | ||||
666 | # OK we want to save to the topic. If we already decided we can | ||||
667 | # save we do not need to check again | ||||
668 | unless ( $saveThisTopic || | ||||
669 | Foswiki::Func::checkAccessPermission( | ||||
670 | 'CHANGE', $currentWikiName, | ||||
671 | undef, $topic, $web | ||||
672 | ) | ||||
673 | ) | ||||
674 | { | ||||
675 | $message .= | ||||
676 | "Topic $web.$topic was not saved due to lack " . | ||||
677 | "of access rights\n\n"; | ||||
678 | last; | ||||
679 | } | ||||
680 | unless ( $saveThisTopic ) { | ||||
681 | my ($oopsUrl, $loginName, $unlockTime) = | ||||
682 | Foswiki::Func::checkTopicEditLock( $web, $topic, undef ); | ||||
683 | my $lockedWikiName = Foswiki::Func::getWikiName( $loginName ); | ||||
684 | if ( $unlockTime && | ||||
685 | ( $lockedWikiName ne $currentWikiName ) ) { | ||||
686 | $message .= | ||||
687 | "Topic $web.$topic was not saved because it is" . | ||||
688 | "currently being edited by !$lockedWikiName\n\n"; | ||||
689 | last; | ||||
690 | } | ||||
691 | } | ||||
692 | if ( $fieldName eq 'text' ) { | ||||
693 | $text = $value; | ||||
694 | } | ||||
695 | else { | ||||
696 | $meta->putKeyed( 'FIELD', { name => $fieldName, value => $value } ); | ||||
697 | } | ||||
698 | |||||
699 | $saveThisTopic = 1; | ||||
700 | } | ||||
701 | } | ||||
702 | |||||
703 | if ( $saveThisTopic ) { | ||||
704 | Foswiki::Func::saveTopic($web, $topic, $meta, $text); | ||||
705 | |||||
706 | #We need to avoid leaving many topics in locked state. | ||||
707 | Foswiki::Func::setTopicEditLock( $web, $topic, 0 ); | ||||
708 | |||||
709 | $topicsavecounter++; | ||||
710 | } | ||||
711 | } | ||||
712 | |||||
713 | $message .= "Number of topics changed: $topicsavecounter"; | ||||
714 | |||||
715 | $query->param(-name => 'MULTITOPICSAVEMESSAGE', -value => "$message"); | ||||
716 | Foswiki::Func::redirectCgiQuery( undef, $url, 1 ); | ||||
717 | return undef; | ||||
718 | } | ||||
719 | |||||
720 | 1 | 3µs | 1; | ||
721 | |||||
722 | __END__ |