]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/WikiFormRich.php
Harmonize file footer
[SourceForge/phpwiki.git] / lib / plugin / WikiFormRich.php
1 <?php // -*-php-*-
2 // rcs_id('$Id$');
3 /*
4  * Copyright 2004,2006,2007 $ThePhpWikiProgrammingTeam
5  *
6  * This file is part of PhpWiki.
7  *
8  * PhpWiki is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * PhpWiki is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with PhpWiki; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 /**
24  * This is another replacement for MagicPhpWikiURL forms.
25  * Previously encoded with the "phpwiki:" syntax.
26  *
27  * Enhanced WikiForm to be more generic:
28  * - editbox[]                 name=.. value=.. text=.. autocomplete=1
29  * - checkbox[]         name=.. value=0|1 checked text=..
30  * - radio[]                 name=.. value=.. text=..
31  * - pulldown[]                name=.. value=.. selected=.. text=.. autocomplete=1
32  * - combobox[]                name=.. value=.. text=.. method=.. args=..
33  * - hidden[]                name=.. value=..
34  * - submit[]
35  * - action, submit buttontext, optional cancel button (bool)
36  * - method=get or post, Default: post.
37
38  * Valid arguments for pulldown and editbox: autocomplete=1, Default: 0
39  * If autocomplete=1, additional arguments method and args may be used.
40  * If no method is given, value will be used to fill in the valid values.
41  * method="xmlrpc:server:name" or "url:http://server/wiki/method" or "array:jsvariable"
42  * or "plugin:pluginname"
43  * args are optional arguments, space seperated, for the method.
44  * A combobox is a pulldown with autocomplete=1.
45  *
46  * @Author: Reini Urban
47
48  * Values which are constants are evaluated.
49  * The cancel button must be supported by the action.
50  *   (just some wikiadmin actions so far)
51  * improve layout by: nobr=1
52  * some allow values as list from from <!plugin-list !>
53
54  Samples:
55    <<WikiFormRich action=dumpserial method=get
56             checkbox[] name=include value="all"
57             editbox[] name=directory value=DEFAULT_DUMP_DIR
58             editbox[] name=pages value=*
59             editbox[] name=exclude value="" >>
60    <<WikiFormRich action=dumphtml method=get
61             editbox[] name=directory value=HTML_DUMP_DIR
62             editbox[] name=pages value="*"
63             editbox[] name=exclude value="" >>
64    <<WikiFormRich action=loadfile method=get
65             editbox[]  name=source value=DEFAULT_WIKI_PGSRC
66             checkbox[] name=overwrite value=1
67             editbox[]  name=exclude value="" >>
68   <<WikiFormRich action=TitleSearch method=get class=wikiadmin nobr=1
69              editbox[] name=s text=""
70            submit[]
71              checkbox[] name=case_exact
72              checkbox[] name=regex >>
73   <<WikiFormRich action=FullTextSearch method=get class=wikiadmin nobr=1
74              editbox[] name=s text=""
75            submit[]
76              checkbox[] name=case_exact
77              checkbox[] name=regex >>
78   <<WikiFormRich action=FuzzyPages method=get class=wikiadmin nobr=1
79              editbox[] name=s text=""
80            submit[]
81              checkbox[] name=case_exact ?>
82   <<WikiFormRich action=AppendText buttontext="AddPlugin"
83              radio[] name=s value=<!plugin-list BackLinks page=WikiPlugin limit=10 !>
84              ?>
85   <<WikiFormRich action=AppendText buttontext="AddPlugin"
86              pulldown[] name=s text="Plugins: " value=<!plugin-list BackLinks page=WikiPlugin !>
87              ?>
88   <<WikiFormRich action=AppendText buttontext="AddCategory"
89              pulldown[] name=s text="Categories: " value=<!plugin-list TitleSearch s=Category !>
90              ?>
91   <<WikiFormRich action=SemanticSearch buttontext="AddRelation"
92              combobox[] name=relation text="Relation: " method=listRelations
93              ?>
94   <<WikiFormRich action=AppendText buttontext="InsertTemplate"
95              combobox[] name=s text="Template: " method=titleSearch args="Template/"
96              ?>
97 */
98
99 class WikiPlugin_WikiFormRich
100 extends WikiPlugin
101 {
102     function getName () {
103         return "WikiFormRich";
104     }
105     function getDescription () {
106         return _("Provide generic WikiForm input buttons");
107     }
108     function getDefaultArguments() {
109         return array('action' => false,     // required argument
110                      'method' => 'post',    // or get
111                      'class'  => 'wikiaction',
112                      'buttontext' => false, // for the submit button. default: action
113                      'cancel' => false,     // boolean if the action supports cancel also
114                      'nobr' => false,       // "no break": linebreaks or not
115                      );
116     }
117
118     /* TODO: support better block alignment: <br>, tables, indent
119      */
120     function handle_plugin_args_cruft($argstr, $args) {
121             $allowed = array("editbox", "hidden", "checkbox", "radiobutton"/*deprecated*/,
122                              "radio", "pulldown", "submit", "reset", "combobox");
123             // no editbox[] = array(...) allowed (space)
124             $arg_array = preg_split("/\n/", $argstr);
125             // for security we should check this better
126         $arg = '';
127             for ($i = 0; $i < count($arg_array); $i++) {
128                 //TODO: we require an name=value pair here, but submit may go without also.
129                 if (preg_match("/^\s*(".join("|",$allowed).")\[\](.*)$/", $arg_array[$i], $m)) {
130                         $name = $m[1]; // one of the allowed input types
131                 $this->inputbox[][$name] = array(); $j = count($this->inputbox) - 1;
132                 $curargs = trim($m[2]);
133                 // must match name=NAME and also value=<!plugin-list name !>
134                 while (preg_match("/^(\w+?)=((?:\".*?\")|(?:\w+)|(?:\"?<!plugin-list.+?!>\"?))\s*/",
135                                   $curargs, $m)) {
136                     $attr = $m[1]; $value = $m[2];
137                     $curargs = substr($curargs, strlen($m[0]));
138                     if (preg_match("/^\"(.*)\"$/", $value, $m))
139                         $value = $m[1];
140                     if (in_array($name, array("pulldown","checkbox","radio","radiobutton","combobox"))
141                             and preg_match('/^<!plugin-list.+!>$/', $value, $m))
142                         // like pulldown[] name=test value=<!plugin-list BackLinks page=HomePage!>
143                     {
144                             $loader = new WikiPluginLoader();
145                             $markup = null;
146                             $basepage = null;
147                             $plugin_str = preg_replace(array("/^<!/","/!>$/"),array("<?","?>"), $value);
148                             // will return a pagelist object! pulldown,checkbox,radiobutton
149                             $value = $loader->expandPI($plugin_str, $GLOBALS['request'], $markup, $basepage);
150                             if (isa($value, 'PageList'))
151                                 $value = $value->pageNames(); // apply limit
152                             elseif (!is_array($value))
153                                     trigger_error(sprintf("Invalid argument %s ignored", htmlentities($arg_array[$i])),
154                                                   E_USER_WARNING);
155                     }
156                     elseif (defined($value))
157                         $value = constant($value);
158                     $this->inputbox[$j][$name][$attr] = $value;
159                 }
160                         //trigger_error("not yet finished");
161                 //eval('$this->inputbox[]["'.$m[1].'"]='.$m[2].';');
162             } else {
163                         trigger_error(sprintf("Invalid argument %s ignored", htmlentities($arg_array[$i])),
164                                       E_USER_WARNING);
165             }
166             }
167         return;
168     }
169
170     function run($dbi, $argstr, &$request, $basepage) {
171         extract($this->getArgs($argstr, $request));
172         if (empty($action)) {
173             return $this->error(fmt("A required argument '%s' is missing.", "action"));
174         }
175         $form = HTML::form(array('action' => $request->getPostURL(),
176                                  'method' => strtolower($method),
177                                  'class'  => 'wikiformrich',
178                                  'accept-charset' => $GLOBALS['charset']),
179                            HiddenInputs(array('action' => $action)));
180         $nbsp = HTML::Raw('&nbsp;');
181         $already_submit = 0;
182         foreach ($this->inputbox as $inputbox) {
183             foreach ($inputbox as $inputtype => $input) {
184               if ($inputtype == 'radiobutton') $inputtype = 'radio'; // convert from older versions
185               $input['type'] = $inputtype;
186               $text = '';
187               if ($inputtype != 'submit') {
188                   if (empty($input['name']))
189                       return $this->error(fmt("A required argument '%s' is missing.",
190                                             $inputtype."[][name]"));
191                   if (!isset($input['text'])) $input['text'] = gettext($input['name']);
192                   $text = $input['text'];
193                   unset($input['text']);
194               }
195               switch($inputtype) {
196               case 'checkbox': // text right
197               case 'radio':
198                 if (empty($input['value'])) $input['value'] = 1;
199                 if (is_array($input['value'])) {
200                     $div = HTML::div(array('class' => $class));
201                     $values = $input['value'];
202                     $name = $input['name'];
203                     $input['name'] = $inputtype == 'checkbox' ? $name."[]" : $name;
204                     foreach ($values as $val) {
205                         $input['value'] = $val;
206                         if ($request->getArg($name)) {
207                             if ($request->getArg($name) == $val)
208                                 $input['checked'] = 'checked';
209                             else
210                                 unset($input['checked']);
211                         }
212                         $div->pushContent(HTML::input($input), $nbsp, $val, $nbsp, "\n");
213                         if (!$nobr)
214                             $div->pushContent(HTML::br());
215                     }
216                     $form->pushContent($div);
217                 } else {
218                     if (empty($input['checked'])) {
219                         if ($request->getArg($input['name']))
220                             $input['checked'] = 'checked';
221                     } else {
222                         $input['checked'] = 'checked';
223                     }
224                     if ($nobr)
225                         $form->pushContent(HTML::input($input), $nbsp, $text, $nbsp);
226                     else
227                         $form->pushContent(HTML::div(array('class' => $class), HTML::input($input), $nbsp, $text));
228                 }
229                 break;
230               case 'editbox': // text left
231                   $input['type'] = 'text';
232                   if (empty($input['value']) and ($s = $request->getArg($input['name'])))
233                       $input['value'] = $s;
234                   if (!empty($input['autocomplete']))
235                       $this->_doautocomplete($form, $inputtype, $input, $input['value']);
236                   if ($nobr)
237                       $form->pushContent($text, $nbsp, HTML::input($input));
238                   else
239                       $form->pushContent(HTML::div(array('class' => $class), $text, $nbsp, HTML::input($input)));
240                   break;
241               case 'combobox': // text left
242                   $input['autocomplete'] = 1;
243               case 'pulldown':
244                   $values = @$input['value'];
245                   unset($input['value']);
246                   unset($input['type']);
247                   if (is_string($values)) $values = explode(",", $values);
248                   if (!empty($input['autocomplete']))
249                       $this->_doautocomplete($form, $inputtype, $input, $values);
250                   $select = HTML::select($input);
251                   if (empty($values) and ($s = $request->getArg($input['name']))) {
252                       $select->pushContent(HTML::option(array('value'=> $s), $s));
253                   } elseif (is_array($values)) {
254                       $name = $input['name'];
255                       unset($input['name']);
256                       foreach ($values as $val) {
257                           $input = array('value' => $val);
258                           if ($request->getArg($name)) {
259                               if ($request->getArg($name) == $val)
260                                   $input['selected'] = 'selected';
261                               else
262                                   unset($input['selected']);
263                           }
264                           //TODO: filter uneeded attributes
265                           $select->pushContent(HTML::option($input, $val));
266                       }
267                   } else { // force empty option
268                       $select->pushContent(HTML::option(array(), ''));
269                   }
270                   $form->pushContent($text, $nbsp, $select);
271                   break;
272               case 'reset':
273               case 'hidden':
274                   $form->pushContent(HTML::input($input));
275                   break;
276               // change the order of inputs, by explicitly placing a submit button here.
277               case 'submit': // text right (?)
278                   //$input['type'] = 'submit';
279                   if (empty($input['value'])) $input['value'] = $buttontext ? $buttontext : $action;
280                   unset($input['text']);
281                   if (empty($input['class'])) $input['class'] = $class;
282                   if ($nobr)
283                       $form->pushContent(HTML::input($input), $nbsp, $text, $nbsp);
284                   else
285                       $form->pushContent(HTML::div(array('class' => $class), HTML::input($input), $text));
286                   // unset the default submit button
287                   $already_submit = 1;
288                   break;
289               }
290             }
291         }
292         if ($request->getArg('start_debug'))
293             $form->pushContent(HTML::input
294                                (array('name' => 'start_debug',
295                                       'value' =>  $request->getArg('start_debug'),
296                                       'type'  => 'hidden')));
297         if (!USE_PATH_INFO)
298             $form->pushContent(HiddenInputs(array('pagename' => $basepage)));
299         if (!$already_submit) {
300             if (empty($buttontext)) $buttontext = $action;
301             $submit = Button('submit:', $buttontext, $class);
302             if ($cancel) {
303                 $form->pushContent(HTML::span
304                                    (array('class' => $class),
305                                     $submit,
306                                     Button('submit:cancel', _("Cancel"), $class)));
307             } else {
308                 $form->pushContent(HTML::span(array('class' => $class),
309                                               $submit));
310             }
311         }
312         return $form;
313     }
314
315     function _doautocomplete(&$form, $inputtype, &$input, &$values) {
316         global $request;
317         $input['class'] = "dropdown";
318         $input['acdropdown'] = "true";
319         //$input['autocomplete'] = "OFF";
320         $input['autocomplete_complete'] = "true";
321         // only match begin: autocomplete_matchbegin, or
322         $input['autocomplete_matchsubstring'] = "true";
323         if (empty($values)) {
324             if ($input['method']) {
325                 if (empty($input['args'])) {
326                     if (preg_match("/^(.*?) (.*)$/",$input['method'],$m)) {
327                         $input['method'] = $m[1];
328                         $input['args'] = $m[2];
329                     } else
330                         $input['args'] = null;
331                 }
332                 static $tmpArray = 'tmpArray00';
333                 // deferred remote xmlrpc call
334                 if (string_starts_with($input['method'], "dynxmlrpc:")) {
335                     // how is server + method + args encoding parsed by acdropdown?
336                     $input['autocomplete_list'] = substr($input['method'],3);
337                     if ($input['args'])
338                         $input['autocomplete_list'] .= (" ".$input['args']);
339                 // static xmlrpc call, local only
340                 } elseif (string_starts_with($input['method'], "xmlrpc:")) {
341                     include_once("lib/XmlRpcClient.php");
342                     $values = wiki_xmlrpc_post(substr($input['method'],7), $input['args']);
343                 } elseif (string_starts_with($input['method'], "url:")) {
344                     include_once("lib/HttpClient.php");
345                     $html = HttpClient::quickGet(substr($input['method'],4));
346                     //TODO: how to parse the HTML result into a list?
347                 } elseif (string_starts_with($input['method'], "dynurl:")) {
348                     $input['autocomplete_list'] = substr($input['method'],3);
349                 } elseif (string_starts_with($input['method'], "plugin:")) {
350                     $dbi = $request->getDbh();
351                     $pluginName = substr($input['method'],7);
352                     $basepage = '';
353                     require_once("lib/WikiPlugin.php");
354                     $w = new WikiPluginLoader;
355                     $p = $w->getPlugin($pluginName, false); // second arg?
356                     if (!is_object($p))
357                         trigger_error("invalid input['method'] ".$input['method'], E_USER_WARNING);
358                     $pagelist = $p->run($dbi, @$input['args'], $request, $basepage);
359                     $values = array();
360                     if (is_object($pagelist) and isa($pagelist, 'PageList')) {
361                         foreach ($pagelist->_pages as $page) {
362                             if (is_object($page))
363                                 $values[] = $page->getName();
364                             else
365                                 $values[] = (string)$page;
366                         }
367                     }
368                 } elseif (string_starts_with($input['method'], "array:")) {
369                     // some predefined values (e.g. in a template or themeinfo.php)
370                     $input['autocomplete_list'] = $input['method'];
371                 } else {
372                     trigger_error("invalid input['method'] ".$input['method'], E_USER_WARNING);
373                 }
374                 if (empty($input['autocomplete_list']))
375                 {
376                     $tmpArray++;
377                     $input['autocomplete_list']="array:".$tmpArray;
378                     $svalues = empty($values) ? "" : join("','", $values);
379                     $form->pushContent(JavaScript("var $tmpArray = new Array('".$svalues."')"));
380                 }
381                 if (count($values) == 1)
382                     $input['value'] = $values[0];
383                 else
384                     $input['value'] = "";
385                 unset($input['method']);
386                 unset($input['args']);
387                 //unset($input['autocomplete']);
388             }
389             elseif ($s = $request->getArg($input['name']))
390                 $input['value'] = $s;
391         }
392         return true;
393     }
394 };
395
396 // Local Variables:
397 // mode: php
398 // tab-width: 8
399 // c-basic-offset: 4
400 // c-hanging-comment-ender-p: nil
401 // indent-tabs-mode: nil
402 // End:
403 ?>