Dotclear

source: inc/admin/class.dc.filter.php @ 885:1195048ad3e2

Revision 885:1195048ad3e2, 19.9 KB checked in by franck <carnet.franck.paul@…>, 13 years ago (diff)

Should cope with filters used by plugins

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12
13
14/**
15@ingroup DC_CORE
16@nosubgrouping
17@brief Interface to add extra data to filterset.
18
19Note : the instance will be cloned to ensure dual filter edit fields / displayed fields
20Be sure to enable correct object cloning then, using the __clone method
21*/
22interface dcFilterExtraInterface {
23     /**
24     Return extra data to display in right column
25     
26     @param    core      <b>dcCore</b>       Dotclear core reference
27     @param    form_prefix    <b>string</b>       form prefix to use for parameters
28     */
29     public function getFormContent();
30
31     /**
32     Set dedicated data from form submission
33     (either $_GET or $_POST depending on the context
34     
35     @param    data      <b>array</b>        Data to retrieve information from
36     */
37     public function initializeFromData($data);
38     
39     /**
40     Save data to configuration
41     */
42     public function save();
43     
44     /**
45     Load data from configuration
46     */
47     public function load();
48         
49     /**
50     Update query parameters with given settings
51     @param    params         <b>ArrayObject</b>       Params being sent to query
52     */
53     public function applyFilters($params);
54     
55     
56     /**
57     Update parameters that will be used for inter-forms communications
58     (either query string or hidden fields).
59     The associative has to be updated with $param['key']='value'
60     @param    params         <b>ArrayObject</b>       Params to update
61     */
62     public function updateRequestParams($params);
63}
64
65/**
66@ingroup DC_CORE
67@nosubgrouping
68@brief Dotclear FilterSet class.
69
70Dotclear FilterSet handles filters and columns when displaying items lists.
71*/
72class dcFilterSet {
73
74     protected $lfilters;          /// <b>array</b> lists of defined filters
75     protected $efilters;          /// <b>array</b> lists of defined filters
76     protected $form_prefix;       /// <b>string</b> displayed form prefix
77     protected $action;            /// <b>string</b> form action page
78     protected $hideform;          /// <b>boolean</b> start form display hidden by default or not
79     protected $lextra;            /// <b>string</b> columns form
80     protected $eextra;            /// <b>string</b> columns form
81     protected $name;              /// <b>string</b> fieldset name
82     
83     /**
84     Inits dcFilterSet object
85     
86     @param    core      <b>dcCore</b>       Dotclear core reference
87     @param    form_prefix    <b>string</b>       form prefix to use for parameters
88     */
89     public function __construct($name,$action,$form_prefix="f_") {
90          $this->name = $name;
91          $this->form_prefix=$form_prefix;
92          $this->lfilters = new ArrayObject();
93          $this->efilters = new ArrayObject();
94          $this->action = $action;
95          $this->lextra = null;
96          $this->eextra = null;
97          $this->filtered = false;
98     }
99     
100     /**
101     Adds a new filter to list
102     
103     @param    filter         <b>dcFilter</b>          the filter to add
104     */
105     public function addFilter (Filter $filter) {
106          $filter->setFormPrefix($this->form_prefix);
107          $filter->setFilterSet($this);
108          $this->efilters[$filter->id] = $filter;
109          $this->lfilters[$filter->id] = clone $filter;
110          return $this;
111     }
112     
113     /**
114     Saves user filters to preferences
115     */
116     protected function saveFilters() {
117          $ser = array();
118          $ws = $GLOBALS['core']->auth->user_prefs->addWorkspace('filters');
119          $data = new ArrayObject();
120          $data= $this->getFiltersAsParams($this->efilters);
121          $ws->put($this->name,serialize($data->getArrayCopy()),'string');
122          return $data;
123     }
124     
125     /**
126     Loads user filters from preferences
127     */
128     protected function loadFilters() {
129          $ws = $GLOBALS['core']->auth->user_prefs->addWorkspace('filters');
130          $data = (!is_null($ws->{$this->name})) ? unserialize($ws->{$this->name}) : array();
131          if (is_array($data))
132               return $data;
133          else
134               return array();
135     }
136     
137     /**
138     Updates filters values according to form_data
139     To be called before any call to display() or getForm()
140     
141     @param    form_data <b>array</b>   form values (usually $_GET or $_POST)
142     */
143     protected function initializeFromData ($filters, $extra, $form_data) {
144          $this->hideform = true;
145          foreach ($filters as $filter) {
146               $filter->initializeFromData ($form_data);
147          }
148          if ($extra != null) {
149               $extra-> initializeFromData ($form_data);
150          }
151     }
152     
153     /**
154     Defines additional form in layout (right column)
155     
156     @param    html <b>string</b>       the code to add
157     */
158     public function setExtra($extra)
159     {
160          $this->lextra = $extra;
161          $this->eextra = clone $extra;
162         
163     }
164     
165     /**
166     Returns form fields as hidden fields
167     
168     @return   <b>string</b>  the corresponding html code
169     */
170     public function getFormFieldsAsHidden() {
171          $ret='';
172          $arr = new ArrayObject();
173          foreach ($this->lfilters as $f) {
174               if ($f->isEnabled())
175                    $f->updateRequestParams($arr);
176          }
177          if ($this->lextra != null) {
178               $this->lextra->updateRequestParams($arr);
179          }
180          foreach ($arr as $k=>$v) {
181               $ret.= form::hidden(array($k),$v);
182          }
183          $queryParams = $this->getFiltersAsParams($this->lfilters);
184          if ($this->lextra != null) {
185               $this->lextra->updateRequestParams($queryParams);
186          }
187          $ret .= form::hidden(array($this->form_prefix."query"), http_build_query($queryParams));
188          return $ret;
189     }
190
191     /**
192     Sets up filterset from $get and $post parameters
193     
194     */
195     public function setup($get,$post) {
196          /* Use cases :
197               (1) $post not empty for formfilter fields :
198                    * efilters are set from $post
199                    * lfilters are set from $get
200                    * keep filters div shown
201               (2) $post empty :
202                    * both efilters and lfilters are set from $get
203                    * hide filter div
204          */
205          $action = false;
206          $allowed_actions = array('clear_filters','add','del_','apply','reset');
207          // Fetch each $post parameter to see whether filters are concerned.
208          // Only 1 action at a time is allowed.
209          foreach ($post as $k => $v) {
210               if (strpos($k,$this->form_prefix)===0) {
211                    $tmp = substr($k,strlen($this->form_prefix));
212                    foreach ($allowed_actions as $a) {
213                         if (strpos($tmp,$a)===0) {
214                              $action = $tmp;
215                              break;
216                         }
217                    }
218               }
219          }
220          if ($action !== false) {
221               // Use case (1)
222               if ($action != 'clear_filters' && $action != 'reset')  {
223                    $this->initializeFromData($this->efilters,$this->eextra, $post);
224                    if ($action == 'add'){
225                         if (isset($post['add_filter']) 
226                              && isset($this->efilters[$post['add_filter']])) {
227                         $this->efilters[$post['add_filter']]->add();
228                         }
229                    } elseif (strpos($action,'del_') === 0) {
230                         $count = preg_match('#del_(.+)_([0-9]+)#',$action,$match);
231                         if (($count == 1) && isset($this->efilters[$match[1]])) {
232                              $this->efilters[$match[1]]->remove($match[2]);
233                         }
234                    } elseif ($action=="apply") {
235                         $data = $this->saveFilters();
236                         if ($this->eextra != null) {
237                              $this->eextra->save();
238                         }
239                         http::redirect($this->action.(strpos($this->action,'?') === 0 ? '?' : '&').http_build_query($data,'','&'));
240                         exit;
241                    }
242               }
243               if (isset($post[$this->form_prefix."query"])) {
244                    parse_str($post[$this->form_prefix."query"],$out);
245                    $this->initializeFromData($this->lfilters,$this->lextra, $out);
246                    if ($action == 'reset') {
247                         $this->initializeFromData($this->efilters,$this->eextra, $out);
248                    }
249               }
250               $this->hideform=false;
251          } else {
252               // Use case (2)
253               $load_from_settings = true;
254               foreach($get as $k=>$v) {
255                    if (strpos($k,$this->form_prefix)===0) {
256                         $load_from_settings=false;
257                         break;
258                    }
259               }
260               if ($load_from_settings) {
261                    $get = new ArrayObject(array_merge($this->loadFilters(),$get));
262               }
263               $this->initializeFromData($this->efilters, $this->eextra, $get);
264               $this->initializeFromData($this->lfilters, $this->lextra, $get);
265
266          }
267     }
268     /**
269     Retrieves filterset generated form
270     
271     @param    method    <b>string</b>       form method to use (default: "get")
272     */
273     public function getForm() {
274          $ret = '';
275         
276          if ($this->hideform) {
277               $formclass = ' class="hidden"';
278               $toggleclass = '';
279          } else {
280               $formclass='';
281               $toggleclass = ' class="opened"';
282          }
283         
284          $ret .= '<p>'.
285               '<a href="#" id="toggle-filters"'.$toggleclass.'>'.
286               __('Toggle filters and display options').
287               '</a></p>'.
288               '<div class="two-cols">'.
289               '<form id="filters" action="'.$this->action.'" method="post"'.$formclass.'>'.
290               '<div class="col70">'.
291               '<h3>'.__('Entries filters').'</h3>'.
292               '<table summary="'.__('Query filters').'" id="tfilters">'.
293               '<tbody>';
294          $count=0;
295          $form_combo=array();
296          $form_combo['-']='';
297          if (count($this->efilters)) {
298               foreach ($this->efilters as $filter) {
299                    if ($filter->isEnabled()) {
300                         $ret .= $filter->getFormLine();
301                    }
302                    $form_combo[$filter->name]=$filter->id;
303                    $count++;
304               }
305          }
306          $ret .= '</tbody></table>'.
307               '<h3 class="margintop">'.__('Add a filter').'</h3>'.
308               '<p id="available_filters">'.
309               form::combo("add_filter",$form_combo).
310               '<input type="submit" value=" + " title="'.__('Add this filter').'" name="'.$this->form_prefix.'add" />'.
311               '</p>'.
312               '<p class="clear"><input class="delete" type="submit" value="'.__('Delete all filters').'" name="'.
313               $this->form_prefix.'clear_filters" />'.
314               '&nbsp;<input  type="submit" value="'.__('Reset').'" name="'.
315               $this->form_prefix.'reset" /></p>'.
316               '</div>';
317          if ($this->eextra != '') {
318               $ret .=
319                    '<div class="col30">'.
320                    $this->eextra->getFormContent().
321                    '</div>';
322          }
323          $queryParams = $this->getFiltersAsParams($this->lfilters);
324          if ($this->lextra != null) {
325               $this->lextra->updateRequestParams($queryParams);
326          }
327         
328          $ret .=
329               '<p class="clear margintop">'.
330               '<input type="submit" value="'.__('Apply filters and display options').
331               '" name="'.$this->form_prefix.'apply" /></p>'.
332               form::hidden(array($this->form_prefix."query"),http_build_query($queryParams)).
333               $GLOBALS['core']->formNonce().
334               '</form>'.
335               '</div>';
336          return $ret;
337     }
338     /**
339     Retrieves the filters values as parameters
340     
341     @param    filters   <b>array</b>   list of concerned filters
342     
343     @return   <b>array</b>   the array of parameters
344
345     */
346
347     protected function getFiltersAsParams($filters) {
348          $arr = new ArrayObject();
349          foreach ($filters as $f) {
350               if ($f->isEnabled())
351                    $f->updateRequestParams($arr);
352          }
353          return $arr;
354     }
355     
356     public function getFiltersText() {
357          $ret = '<p>'.__('Currently applied filters :').'</p><ul>';
358          foreach ($this->lfilters as $f) {
359               if ($f->isEnabled())
360                    $ret .= '<li>'.$f->getAsText().'</li>'."\n";
361          }
362          $ret .= '</ul>';
363          return $ret;
364     }
365     
366     /**
367     Displays required fieldset http header
368     To be called in page header, of course.
369     */
370     public function header() {
371          $ret = dcPage::jsLoad('js/filters.js');
372          foreach($this->efilters as $f) {
373               $ret .= $f->header();
374          }
375          return $ret;
376     }
377     
378     
379     /**
380     Displays the fieldset
381     */
382     public function display() {
383          echo $this->getForm();
384     }
385
386     /**
387     Applies fieldset and return resulting parameters for request
388     
389     @param    method    <b>string</b>       form method to use (default: "get")
390     @param    method    <b>string</b>       form method to use (default: "get")
391     
392     */
393     public function applyFilters($params) {
394          foreach ($this->lfilters as $filter) {
395               if ($filter->isEnabled()) {
396                    $filter->applyFilter($params);
397                    $this->filtered = true;
398               }
399          }
400          if ($this->lextra != null) {
401               $this->lextra->applyFilters($params);
402          }
403          return $this->filtered;
404     }
405     
406     public function getDelName($field_id,$pos) {
407          return $this->form_prefix.'del_'.$field_id.'_'.$pos;
408     }
409}
410
411
412/**
413@ingroup DC_CORE
414@nosubgrouping
415@brief abstract filter class.
416
417Dotclear Filter handles administration filters for each list
418A filter fills in a parameter array, as defined in dcBlog class
419*/
420abstract class Filter {
421     public $filterset;            ///<b>string</b> filterset parent
422     public $id;                        ///<b>string</b> field id (local to fieldset)
423     public $name;                 ///<b>string</b> filter name
424     public $desc;                 ///<b>string</b> field description
425     protected $request_param;     ///<b>string</b> resulting parameter array key
426     protected $enabled;           ///<b>string</b> true if filter is enabled
427     protected $values;            ///<b>array</b> possible filter values
428     public $field_id;             ///<b>string</b> field id (global to the page)
429     
430     /**
431     Inits Filter object
432     
433     @param    id        <b>string</b>  field id
434     @param    form_prefix    <b>string</b>       form prefix to use for parameters
435     */
436     public function __construct ($id,$name,$desc,$request_param) {
437          $this->id = $id;
438          $this->name=$name;
439          $this->desc = $desc;
440          $this->request_param = $request_param;
441          $this->enabled=false;
442          $this->values = array();
443          $this->field_id = $this->id;
444     }
445     
446     /**
447     Defines the filterset containing this filter
448     
449     @param    prefix         <b>dcFilterset</b>  the filterset
450     */
451     public function setFilterSet($fs) {
452          $this->filterset = $fs;
453     }
454     
455     /**
456     Defines form prefix for filter
457     
458     @param    prefix         <b>string</b>  the form prefix
459     */
460     public function setFormPrefix($prefix) {
461          $this->field_id = $prefix.$this->id;
462     }
463     
464     /**
465     Get a field id
466     
467     @param    pos       <b>integer</b> position of field, in case of multiple field (0 if only 1 field set, default value)
468     @return   <b>string</b> The field ID
469     */
470     protected function getFieldId($pos=0) {
471          if ($pos == 0) {
472               return $this->field_id;
473          } else {
474               return $this->field_id.'_'.$pos;
475          }
476     }
477     
478     /**
479     Tells whether the filter is enabled or not
480     
481     @return   <b>boolean</b> true if enabled, false otherwise
482     */
483     public function isEnabled() {
484          return $this->enabled;
485     }
486     
487     /**
488     Adds the current filter to the list
489     */
490     public function add() {
491          // By default here, only 1 value allowed. Simply enable the filter
492          $this->enabled = true;
493     }
494     
495     /**
496     Removes a value from filter
497     */
498     public function remove($pos) {
499          if (isset($this->values[$pos])) {
500               array_splice($this->values,$pos,1);
501               $this->enabled = (count($this->values)!=0);
502          }
503     }
504     
505     /**
506     Returns HTML code for form field
507     
508     @param    pos       <b>integer</b> position of the field to display
509                                             (in case of multiple values)
510     @return <b>string</b> the html code
511     */
512     abstract protected function getFormFields($pos=0);
513     
514     /**
515     Extract values from data (data being an array, such as $_GET or $_POST)
516     
517     @param    $data     <b>array</b>   data to parse
518     @return   <b>array</b>   field values
519     
520     */
521     protected function getValuesFromData($data) {
522          $count=0;
523          $arr = array();
524          while (isset($data[$this->getFieldId($count)])) {
525               $arr[$count] = $data[$this->getFieldId($count)];
526               $count++;
527          }
528          return $arr;
529     }
530     
531     public function initializeFromData($form_data) {
532          $this->values = $this->getValuesFromData($form_data);
533          $this->enabled = (count($this->values)!=0);
534     }
535     
536     /**
537     Returns HTML code for the hole filter lines
538     
539     @return <b>string</b> the html code
540     */
541     
542     public function getFormLine() {
543          $ret='';
544          for ($cur=0; $cur < count($this->values); $cur++) {
545               $ret .= '<tr class="'.$this->id.'">';
546               $del_id = $this->filterset->getDelName($this->id,$cur);
547               $ret .= '<td><input id="'.$del_id.'" class="delete" '.
548                         'type="submit" title="Delete the following filter : " value=" - " name="'.$del_id.'"/></td>'.
549                         $this->getFormFields($cur);
550               $ret .= '</tr>';
551          }
552          return $ret;
553     }
554     
555     public function updateRequestParams($arr) {
556          for ($cur=0; $cur < count($this->values); $cur++) {
557               $arr[$this->getFieldId($cur)]=$this->values[$cur];
558          }
559     }
560     
561     /**
562     Convert filter values into a $param filter, used for the upcoming SQL request
563     
564     @param <b>ArrayObject</b> the parameters array to enrich
565     */
566     public function applyFilter($params) {
567     }
568     
569     public function setValues($value) {
570          $this->values = $value;
571     }
572     
573     public function getValue() {
574          return $this->values;
575     }
576
577     public function header() {
578          return '';
579     }
580     
581     public abstract function getAsText();
582
583     
584}
585
586/**
587@ingroup DC_CORE
588@nosubgrouping
589@brief abstract filter class.
590
591Handle combo filter on admin side. Can be single or multi-valued
592*/
593class comboFilter extends Filter {
594     protected $options;
595     protected $default;
596     protected $no_value;
597     protected $verb;
598     protected $extra;
599     
600     public function __construct($id,$name,$desc,$request_param,$options,$extra=array()) {
601          parent::__construct($id,$name,$desc,$request_param);
602          $this->options = $options;
603          $this->extra = $extra;
604          $this->verb = "is";
605          $this->values=array();
606     }
607     
608     protected function getValuesFromData($data) {
609          $val = parent::getValuesFromData($data);
610          if (isset($data[$this->field_id.'_v'])) {
611               $verb = $data[$this->field_id.'_v'];
612          } else {
613               $verb = "is";
614          }
615          $arr = array(
616               'values' => $val,
617               'verb' => $verb
618          );
619          return $arr;
620     }
621     
622     public function add() {
623          parent::add();
624          if (isset($this->extra['singleval']) && (count($this->values) > 0))
625               return;
626          $this->values[]=current($this->options);
627     }
628     
629     public function getType() {
630          return "combo";
631     }
632
633     public function serialize() {
634          $data = parent::serialize();
635          $data['verb'] = $this->verb;
636          return $data;
637     }
638     
639     public function unserialize ($data) {
640          parent::unserialize($data);
641          $this->verb = $data['verb'];
642     }
643     
644     public function initializeFromData($form_data) {
645          $arr = $this->getValuesFromData($form_data);
646          $this->values = $arr['values'];
647          $this->verb = $arr['verb'];
648          $this->enabled = (count($this->values) != 0);
649     }
650
651     public function getFormFields($pos=0) {
652          if ($pos == 0) {
653               $ret = '<td id="'.$this->getFieldId($pos).'" title="'.$this->desc.'" class="filter-title">'.
654                    ''.$this->desc.' : </td>'.
655                    '<td>'.
656                    form::combo($this->field_id.'_v',
657                         array(__('is')=>'is',__('is not')=>'isnot'),$this->verb,'','',
658                         false,'title="'.sprintf(__('%s is or is not'),$this->desc).'"').
659                    '</td>';
660          } else {
661               $ret = '<td id="'.$this->getFieldId($pos).'" title="or" colspan="2" class="or">'.
662                    __('or').' : </td>';
663          };
664          $ret .= '<td>'.form::combo($this->getFieldId($pos),$this->options,$this->values[$pos],
665               '','',false,'title="'.__('Choose an option').'"').'</td>';
666          return $ret;
667     }
668     
669     public function updateRequestParams($arr) {
670          parent::updateRequestParams($arr);
671         
672          $arr[$this->field_id.'_v']=$this->verb;
673     }
674     
675     public function applyFilter($params) {
676          $attr = $this->request_param;
677          if ($this->verb != "is") {
678               $params[$attr."_not"] = true;
679          }
680          if (isset($this->extra['singleval']))
681               $params[$attr]=$this->values[0];
682          else
683               $params[$attr]=$this->values;
684}
685     
686     public function getValues() {
687          return array_merge($this->values,array($this->field_id.'_v',$this->verb));
688     }
689     
690     public function getAsText() {
691          $arr=array();
692          foreach ($this->values as $value) {
693               $arr[]=array_search($value,$this->options);
694          }
695          return sprintf("%s %s %s",$this->desc,$this->verb,join(',',$arr));
696     }
697}
698
699
700class categoryFilter extends comboFilter {
701     public function getAsText() {
702          $arr=array();
703          foreach ($this->values as $value) {
704               $cat=array_search($value,$this->options);
705               $arr[]=preg_replace("#^.* ([^ ]+) .*$#",'$1',$cat);
706          }
707          return sprintf("%s %s %s",$this->desc,$this->verb,join(',',$arr));
708     }
709}
710/**
711@ingroup DC_CORE
712@nosubgrouping
713@brief abstract filter class.
714
715Handle boolean filter on admin side.
716*/
717class booleanFilter extends Filter {
718     protected $options;
719     
720     public function __construct($id,$name,$desc,$request_param,$options) {
721          parent::__construct($id,$name,$desc,$request_param);
722          $this->options = $options;
723          $this->values=array();
724     }
725     
726     
727     public function getType() {
728          return "boolean";
729     }
730     public function add() {
731          parent::add();
732          $this->values=current($this->options);
733     }
734
735     public function getFormFields($pos=0) {
736          return '<td colspan="2">'.$this->desc.'</td><td>'.
737               form::combo($this->getFieldId($pos),$this->options,$this->values[$pos],
738                    '','',false,'title="'.__('Choose an option').'"').'</td>';
739     }
740     
741     public function applyFilter($params) {
742          $params[$this->request_param]=$this->values[0];
743     }
744     
745     public function getAsText() {
746          return sprintf("%s %s",$this->desc,$this->values[0]);
747     }
748}
749
750
751class textFilter extends Filter {
752     protected $size;
753     protected $max;
754     
755     public function __construct($id,$name,$desc,$request_param,$size,$max) {
756          parent::__construct($id,$name,$desc,$request_param);
757          $this->size = $size;
758          $this->max = $max;
759          $this->values=array();
760     }
761     
762     
763     public function getType() {
764          return "text";
765     }
766     public function add() {
767          parent::add();
768          $this->values[]='';
769     }
770
771     public function getFormFields($pos=0) {
772          return '<td colspan="2">'.$this->desc.'</td><td>'.
773               form::field($this->getFieldId($pos),$this->size,$this->max,html::escapeHTML($this->values[0])).
774               '</td>';
775     }
776     
777     public function applyFilter($params) {
778          $params[$this->request_param]=$this->values[0];
779     }
780     
781     public function setValues($value) {
782          parent::setValues(array($value));
783     }
784
785     public function getAsText() {
786          return sprintf("%s %s",$this->desc,$this->values[0]);
787     }
788     
789}
790
791
792?>
Note: See TracBrowser for help on using the repository browser.

Sites map