Framework  3.9
filter_form.inc
Go to the documentation of this file.
1 <?php
5 /**************************************************************
6 
7  Copyright (c) 2007-2010 Sonjara, Inc
8 
9  Permission is hereby granted, free of charge, to any person
10  obtaining a copy of this software and associated documentation
11  files (the "Software"), to deal in the Software without
12  restriction, including without limitation the rights to use,
13  copy, modify, merge, publish, distribute, sublicense, and/or sell
14  copies of the Software, and to permit persons to whom the
15  Software is furnished to do so, subject to the following
16  conditions:
17 
18  The above copyright notice and this permission notice shall be
19  included in all copies or substantial portions of the Software.
20 
21  Except as contained in this notice, the name(s) of the above
22  copyright holders shall not be used in advertising or otherwise
23  to promote the sale, use or other dealings in this Software
24  without prior written authorization.
25 
26  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
28  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
30  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
31  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33  OTHER DEALINGS IN THE SOFTWARE.
34 
35 *****************************************************************/
36 
37 require_once realpath(dirname(__FILE__)."/search_form.inc");
38 require_once realpath(dirname(__FILE__)."/auto_form_layout.inc");
39 require_once realpath(dirname(__FILE__)."/field_renderers/select_field_renderer.inc");
40 require_once realpath(dirname(__FILE__)."/field_renderers/date_field_renderer.inc");
41 require_once realpath(dirname(__FILE__)."/field_renderers/boolean_field_renderer.inc");
42 require_once realpath(dirname(__FILE__)."/field_renderers/checklist_field_renderer.inc");
43 
44 
120 class FilterForm extends SearchForm
121 {
122  var $preserveQueryString = false;
123  var $navigationFunction = "go('{url}')";
124  var $navigationMode = "form";
125  var $showSubmitButton = false;
126  var $submitButtonLabel = "Filter Results";
127  var $formLabel = null;
128  var $rememberFilter = false;
129 
130  function FilterForm($target, $method="GET", $action="", $id="")
131  {
132  parent::SearchForm($target, $method, $action, $id);
133  $this->layout = new FilterFormLayout($this);
134  $this->formCSS = "filter";
135  $this->setFilterParameters();
136  }
137 
138  function getLink($url)
139  {
140  return str_replace("{url}", $url, $this->navigationFunction);
141  }
142 
147  function setNavigationMode($mode)
148  {
149  $this->navigationMode = $mode;
150  if ($mode == "panel")
151  {
152  $this->navigationFunction = "loadPanel(form, '{url}');";
153  $this->onSubmitHandler = "{$this->id}_loadPanel";
154  $this->action = Fakoli::scriptName();
155  }
156  }
157 
159  {
160  return "onSubmit{$this->id}";
161  }
162 
163  function writeScript()
164  {
165  if ($this->navigationMode == "panel")
166  {
167  $script = <<<ENDSCRIPT
168 <script type="text/javascript">
169 function {$this->id}_loadPanel(form)
170 {
171  var form = new Element(form);
172  var url = form.action + "?" + form.toQueryString();
173  form.loadPanel(url);
174  return false;
175 }
176 </script>
177 ENDSCRIPT;
178  }
179  return $script . parent::writeScript();
180  }
181 
182  private function sessionKey()
183  {
184  $base = preg_replace("/\\?.*/", "", $_SERVER["REQUEST_URI"]);
185  return "filter:{$this->id}:{$base}";
186  }
187 
188  private function loadFilterFromSession()
189  {
190  if (isset($_SESSION[$this->sessionKey()]))
191  {
192  $this->params->copy(unserialize($_SESSION[$this->sessionKey()]));
193  $this->data =& $this->params->target;
194  }
195  }
196 
197  private function saveFilterToSession()
198  {
199  if (!$this->params->isDefaultState)
200  {
201  $_SESSION[$this->sessionKey()] = serialize($this->params);
202  }
203  else
204  {
205  unset($_SESSION[$this->sessionKey()]);
206  }
207  }
208 
209  function drawForm()
210  {
211  $obj =& $this->data;
212  $pk = $obj->getPrimaryKey();
213  if ($this->preserveQueryString)
214  {
215  $obj->fromREQUEST();
216  }
217 
218 // echo "<p>Empty: {$this->params->empty}</p>";
219 // echo "<p>Default State: {$this->params->isDefaultState}</p>";
220 // echo "<p>".$this->sessionKey()."</p>";
221 // echo "<pre>".$_SESSION[$this->sessionKey()]."</pre>";
222  echo "<form id='{$this->id}' method='{$this->method}' action='{$this->action}' enctype='multipart/form-data'";
223  if (!$this->ajaxSubmitHandler)
224  {
225  echo " onsubmit='return onSubmit{$this->id}(this);'";
226  }
227 
228  if ($this->navigationMode == 'panel')
229  {
230  echo " data-mode='ajax'";
231  }
232  echo ">\n";
233 
234  echo "<div";
235  if ($this->formCSS) echo " class='{$this->formCSS}'";
236  if ($this->style) echo " style='{$this->style}'";
237  echo ">\n";
238 
239  foreach(array_keys($this->hidden) as $hidden)
240  {
241  if ($obj->filter && $obj->filter->isExcluded($hidden)) continue;
242 
243  echo "<input id='{$this->id}_{$hidden}' type='hidden' name='$hidden' value='".htmlSafe($obj->get($hidden))."'/>\n";
244  }
245 
246  if ($this->preserveQueryString)
247  {
248  foreach($_GET as $key => $value)
249  {
250  if ($key == "identifier" || $key == "section" || $key == "submit" || array_key_exists($this->hidden, $key) || $obj->hasField($key)) continue;
251  echo "<input id='{$this->id}_{$key}' type='hidden' name='$key' value='".htmlSafe($value)."'/>\n";
252  }
253  }
254 
255  $this->layout->startUngrouped();
256 
257  if ($this->formLabel) echo "<div class='filter_form_label'><label>{$this->formLabel}</label></div>";
258  $this->renderSearchFields();
259  if ($this->showSubmitButton)
260  {
261  echo "<input type='submit' class='button' value='{$this->submitButtonLabel}'/>";
262  }
263  else
264  {
265  // Hidden submit button allow filter to be run by hitting enter
266  echo "<input type='submit' style='display: none'/>";
267  }
268 
269  $this->layout->endUngrouped();
270 
271  $this->layout->finalizeLayout();
272  echo "</div>\n";
273  echo "</form>\n";
274  echo "<div style=\"clear: both\">\n</div>\n";
275  }
276 
277  /*
278  * If the form contains a DateRangeFilterRenderer,
279  * it must be rendered last.
280  */
282  {
283  $obj =& $this->data;
284  $filter = $obj->getFilter();
285 
286  if (count($this->groups) > 0)
287  {
288  $this->layout->endUngrouped();
289 
290  foreach($this->groups as $legend => $fields)
291  {
292  $collapsible = array_key_exists($legend, $this->collapsibleGroups);
293  if ($collapsible)
294  {
295  $cf = $this->collapsibleGroups[$legend];
296  $collapsed = !$this->data->$cf;
297  }
298 
299  $this->layout->startGroup($legend, $collapsible, $collapsed);
300 
301  foreach($fields as $field)
302  {
303  if ($field != $pk && !array_key_exists($field, $this->hidden) && !array_key_exists($field, $this->renderedFields) && !($filter && $filter->isExcluded($field)))
304  {
305  $this->renderOneSearchField($field);
306  }
307  }
308 
309  $this->layout->endGroup();
310  }
311 
312  $this->layout->startUngrouped();
313 
314  }
315 
316  foreach(array_keys($this->fields) as $field)
317  {
318  if ($field != $pk && !array_key_exists($field, $this->hidden) && !array_key_exists($field, $this->renderedFields) && !($filter && $filter->isExcluded($field)))
319  {
320  $renderer = $this->getRenderer($field);
321  if(get_class($renderer) == DateRangeFilterRenderer)
322  {
323  $dateRangeField = $field;
324  continue;
325  }
326  $this->renderOneSearchField($field);
327  }
328  }
329 
330  foreach($this->additional as $r)
331  {
332  $renderer = $r['renderer'];
333  $field = $r['field'];
334  if(!$field)
335  $field = $renderer->field;
336  if (!array_key_exists($field, $this->renderedFields))
337  {
338  if(get_class($renderer) == DateRangeFilterRenderer)
339  {
340  $dateRangeField = $field;
341  continue;
342  }
343  $this->renderOneSearchField($field);
344  }
345  }
346 
347  if($dateRangeField)
348  {
349  $this->renderOneSearchField($dateRangeField);
350  }
351  }
352 
360  function setHandler($field, $handler)
361  {
362  $this->params->paramHandlers[$field] = $handler;
363  }
364 
365 
374  {
375  $obj = $this->data;
376 
377  // JDG 9/2011 handle CompositeDataItems
378  $fields = $obj->getFields();
379  $this->params = new SearchParameters($obj);
380 
381  foreach(array_keys($fields) as $field)
382  {
383  if (!array_key_exists($field, $this->hidden) &&
384  !($obj->filter && $obj->filter->isExcluded($field))
385  )
386  {
387  $value = $obj->get($field);
388  // AJG - Removing this logic as it is incorrectly re-encoding parameters leading to bad side-effects
389  // Application code that breaks as a result of this should be fixed in the application
390  //if($value && !is_int($value))
391  // $value = urlencode($value);
392  if($value)
393  $this->params->setParam($field, "equal", $value);
394  }
395  }
396  }
397 
398  function getConstraint($first = true, $firstText = "WHERE")
399  {
400  if ($this->rememberFilter)
401  {
402  if ($this->params->isDefaultState)
403  {
404  $this->loadFilterFromSession();
405  }
406  else
407  {
408  $this->saveFilterToSession();
409  }
410  }
411 
412  return $this->params->generateConstraint($first, $firstText);
413  }
414 
415  function setParam($field, $mode, $value)
416  {
417  $this->params->setParam($field, $mode, $value);
418  }
419 
420 }
421 
422 /*
423  * Renders a select field drop down with the option
424  * set given. When one option is selected, the page
425  * reloads with the selected item as the parameter
426  * to be retrieved with "GET".
427  *
428  */
429 class FilterFieldRenderer extends SelectFieldRenderer
430 {
431  var $options;
432  var $label;
433  var $all = true;
434  var $all_value = "";
435  var $width = "";
436  var $cssClass = 'select';
437 
438  function FilterFieldRenderer(&$form, $field, $label = "", $options = null)
439  {
440  $this->options = $options;
441  $this->label = ($label) ? $label : prettify($field);
442  $this->FieldRenderer($form);
443  if ($form->getData()->hasField($field))
444  {
445  // JDG 5/30/11 - don't crash on additiona/psuedo fields
446  // only query if real field
447  $form->override($field, $label, $this);
448  if ($this->options == null)
449  {
450  $opt = $form->getData()->distinctValues($field, true);
451  if (count($opt) > 0)
452  {
453  $this->options = array_combine($opt, $opt);
454  }
455  else
456  {
457  $this->options = array();
458  }
459  }
460  }
461  else
462  {
463  $form->add($this, $field);
464  // JDG 7/22/2011 - allow "additional" fields to override label
465  $form->overrides[$field]['label'] = $label;
466  }
467  }
468 
469  function setDefault($title, $value)
470  {
471  $this->default = $title;
472  $this->defaultValue = $value;
473  }
474 
475  function renderField($field)
476  {
477  $this->_startField($field, $this->styles);
478 
479  if($this->width)
480  {
481  $styles .= "style='width: {$this->width}'";
482  }
483 
484  $onSubmit = $this->parent->getOnSubmitFunction();
485  echo "<select name='{$field}' {$styles} onchange='if ({$onSubmit}(this.form)) this.form.manager.submit();'>";
486 
487  if ($this->default)
488  {
489  ?>
490  <option value="<?php echo $this->defaultValue ?>"><?echo $this->default?></option>
491  <?
492  }
493 
494  if($this->all)
495  {
496  ?>
497  <option value="<?php echo $this->all_value ?>">All</option>
498  <?
499  }
500 
501  $fieldValue = $this->parent->data->get("$field");
502  foreach($this->options as $value => $name)
503  {
504  $name = htmlSafe($name);
505  $trunced = $this->max_chars ? ellipsis($name, $this->max_chars, true) : $name;
506 
507  $selected = (!strcmp($value, $fieldValue)) ? " selected" : "";
508  $optionTitle = ($name != $trunced) ? " title='$name'" : "";
509 
510  echo "<option value='{$value}' $selected>$trunced</option>\n";
511  }
512 
513  echo "</select>&nbsp;";
514 
515  $this->_endField($field);
516  }
517 
518  function renderSearchField($field)
519  {
520  $this->renderField($field);
521  }
522 
523  /*
524  * Given an array of dataitem objects, return
525  * an array of options with the key the primary
526  * key of the obj and the name field the param given.
527  */
528  static function formatOptions($items, $template)
529  {
530  $options = array();
531 
532  // user sent just the field name
533  if(!preg_match("/^\{/", $template))
534  $template = "{" . $template . "}";
535 
536  if(count($items) == 0)
537  return $options;
538 
539  foreach($items as $item)
540  {
541  $pk = $item->getPrimaryKey();
542  $options[$item->$pk] = $item->format("$template");
543  }
544  return $options;
545  }
546 
547 } // end class FilterFieldRenderer
548 
549 
550 /*
551  * For Boolean checkboxes, the unchecked value "0" is not
552  * included in the parameter string. This is a problem when
553  * we want the field to be checked on initial page load or
554  * by default because we can't identify when the box is
555  * unchecked b/c the user unchecked it or because
556  * we just loaded. To fix this problem, we write the checkbox
557  * as field name "field" + "_box" and create a hidden field
558  * with the field name. This hidden field will appear in the
559  * url as 0 if user unchecked the box and 1 if checked.
560  * The hidden field is set using onchange event on the
561  * checkbox html.
562  *
563  $unbilledCheckbox = new BooleanFilterFieldRenderer($filterForm, "ledger_id", "Show Unbilled Only", 1);
564  *
565  * For BooleanFilters, we also may want to use a callback to
566  * generate the clause for this field. E.g:
567  *
568  // Set custom callback handler to generate the constraint for ledger_id
569  $filterForm->setHandler("ledger_id", array(ProjectManager, getLedgerClause));
570 
571  function getLedgerClause($name, $value)
572  {
573  if(!$value && is_numeric($value))
574  return "";
575  else
576  return "{$name}=0";
577  }
578  */
580 {
581  var $cssClass = "boolean";
582  var $default;
583 
591  function BooleanFilterFieldRenderer(&$parent, $field = "", $label = "", $default = 0)
592  {
593  $this->label = ($label) ? $label : prettify($field);
594 
595  $this->default = $default;
596 
597  $this->FieldRenderer($parent);
598  if ($parent->data->hasField($field))
599  {
600  $parent->override($field, $label, $this);
601  }
602  else
603  {
604  $parent->add($this, $field);
605  $parent->overrides[$field]['label'] = $this->label;
606  }
607 
608  $value = $this->getValue($field);
609  // The search parameter value is used for the constraint
610  $this->parent->params->clearParam($field);
611  $this->parent->params->setParam($field, "checked", $value);
612  }
613 
614  private function getValue($field)
615  {
616  if ($this->parent->data->hasField($field))
617  {
618  $value = $this->parent->data->get($field);
619  if(!$value && !is_numeric($value))
620  {
621  return $this->default;
622  }
623  else
624  {
625  return $value;
626  }
627  }
628 
629  if (isset($_REQUEST[$field]))
630  {
631  return checkNumeric($_REQUEST[$field]);
632  }
633 
634  return $this->default;
635  }
636 
637  /*
638  * When the checkbox is checked or unchecked,
639  * update the value of the hidden field.
640  */
641  function renderSearchScript($field)
642  {
643  $selChanged = "selChanged_" . $field . "_box";
644  $onSubmit = $this->parent->getOnSubmitFunction();
645 ?>
646 <script type="text/javascript">
647 
648 function <? echo $selChanged ?>(elt)
649 {
650  var value_field = document.id('<?php echo $field ?>');
651  var new_value = "0";
652 
653  if(value_field && elt)
654  {
655  if(elt.checked == true)
656  new_value = "1";
657 
658  value_field.set("value", new_value);
659  }
660  if (<?php echo $onSubmit?>(elt.form)) elt.form.manager.submit();
661 }
662 </script>
663 <?
664  }
665 
666  function renderSearchField($field)
667  {
668  $this->_startField($field, $this->styles);
669 
670  echo "<input type='checkbox' value='1' name='{$field}_box'";
671 
672  $value = $this->getValue($field);
673 
674  if ($value)
675  {
676  echo " checked='checked'";
677  }
678  echo " onclick='selChanged_{$field}_box(this);'/>\n";
679 
680  if(!$value) $value = 0;
681  echo "<input id='{$field}' type='hidden' name='{$field}' value='$value'/>\n";
682  echo "&nbsp;&nbsp;<label for='{$field}'>{$label}</label>";
683 
684  $this->_endField($field);
685  }
686 }
687 
703 {
704  var $cssClass = 'checklist';
705 
714  function CheckListFilterFieldRenderer(&$form, $field, $label, $options, $defaults = "")
715  {
716  parent::CheckListFieldRenderer($form, $field, $label, $options);
717  $this->horizontal = true;
718 
719  $value = $this->parent->data->get($field);
720  if(!$value && !is_numeric($value) && ((!isset($_GET["{$field}_submitted"]) || !$_GET["{$field}_submitted"])) && $defaults)
721  {
722  $defaults = preg_replace("/'/", "", $defaults);
723  $defaults = array_flip(explode(",", preg_replace("/,\s*/", ",", $defaults)));
724 
725  // The form's data value is used to set the selected checkboxes and cannot contain the "'" around the string values
726  $this->parent->data->set($field, $defaults);
727 
728  if(!is_numeric(array_flip($defaults[0])))
729  {
730  $value = "'" . implode("','", array_keys($defaults)) . "'";
731  }
732  else
733  {
734  $value = implode(",", array_keys($defaults));
735  }
736  }
737 
738  //AJG - Make sure there is no pre-existing parameter that would cause a duplicate
739  $this->parent->params->clearParam($field);
740  // The search parameter's value is used in the constraint and must contain "'" around each string value
741  $this->parent->params->setParam($field, "member", $value);
742  }
743 
744  /*
745  * Draws a horizontal line of checkboxes.
746  */
747  function renderSearchField($field)
748  {
749  $this->_startField($field, $this->styles);
750 
751  $searchValues = $this->parent->data->get($field);
752 
753  if(is_array($searchValues))
754  {
755  $searchValues = explode(",", implode(",", array_keys($searchValues)));
756  }
757  else
758  {
759  $searchValues = array();
760  }
761 
762  $idx = 0;
763 
764  $onSubmit = $this->parent->getOnSubmitFunction();
765  if(count($this->options) > 0)
766  {
767  foreach($this->options as $value => $text)
768  {
769  trace("CheckListFieldRenderer value $value and text $text", 3);
770  echo "<li>\n";
771  $onchange = " onchange='if ({$onSubmitFunction}(this.form)) this.form.manager.submit();'";
772  $checked = (array_search($value, $searchValues) !== FALSE) ? " checked" : "";
773  echo "<input type='checkbox' class='checkbox' name='{$field}[$value]' value='{$value}'$checked$onchange/>&nbsp;$text$divider";
774  $idx++;
775  echo "</li>\n";
776  }
777 
784  echo "<input type='hidden' name='{$field}_submitted' value='1' />";
785  }
786 
787  $this->_endField($field);
788  }
789 
790  function _startField($field, $styles = "")
791  {
792  echo "<div class='{$this->cssClass}'>\n";
793  echo "<ul>\n";
794  echo "<li>\n";
795  $this->parent->layout->printLabel($field, $this, $styles, "");
796  echo "</li>\n";
797  }
798 
799  function _endField($field, $styles = "")
800  {
801  echo "</ul>\n";
802  $this->parent->layout->endField($field, $this);
803  echo "<div style='clear: both'></div>\n";
804  }
805 }
806 
808 {
809  var $cssClass = "radio";
810 
811  function RadioButtonFieldRenderer(&$parent, $field, $label, $options)
812  {
813  parent::RadioButtonFieldRenderer($parent, $field, $label, $options);
814  $this->horizontal = true;
815  }
816 
817  function renderSearchField($field)
818  {
819  $this->_startField($field, $this->styles);
820 
821  $onSubmit = $this->parent->getOnSubmitFunction();
822  $onchange = " onchange='if ({$onSubmit}(this.form)) this.form.manager.submit();'";
823 
824  $searchValue = $this->parent->data->get($field);
825 
826  foreach($this->options as $value => $name)
827  {
828  $selected = ($searchValue == $value) ? " checked" : "";
829  echo "<input style='border: none' type='radio' name='$field' value='$value'$selected$onchange>$name</option>";
830  echo ($this->horizontal) ? "&nbsp;&nbsp;" : "<br/>";
831  }
832 
833  $this->_endField($field);
834  }
835 
836  function _startField($field, $styles = "")
837  {
838  echo "<div class='{$this->cssClass}'>\n";
839  $this->parent->layout->printLabel($field, $this, $styles, "");
840  echo "<ul>\n";
841 
842  }
843 
844  function _endField($field, $styles = "")
845  {
846  echo "</ul>\n";
847  $this->parent->layout->endField($field, $this);
848  echo "<div style='clear: both'></div>\n";
849  }
850 }
851 
899 {
900  var $startParam = "startDate";
901  var $endParam = "endDate";
902  var $stepMode; // month, year or fiscal_year
903  var $fiscal_year_start = 1; // month number on which fiscal year begins
904 
905  var $startDate; // the start date for the filter field
906  var $endDate; // the end date for the filter field
907 
908  // the name of the date fields in the table to be searched
909  // if no endDateField, then search will be field BETWEEN startDate and endDate
910  var $field; // start date field
912 
913  var $stepLabel; // e.g., Next/Previous Month
914  var $dateRangeLabel = "Reporting Period";
915  var $subTitle; // Calling code can optionally output a text description of the date range by calling this renderer through the form.
916 
917  var $handler; // custom parameter handler if override to getConstraint needed
918  var $cssClass = "date_range";
919 
920  function __construct($parent, $field, $endDateField = "", $stepMode = "month", $fiscal_year_start = 1, $startParam = null, $endParam = null)
921  {
922  $this->stepMode = $stepMode;
923  $this->field = $field;
924 
925  $this->endDateField = (!$endDateField) ? $field : $endDateField;
926  $this->fiscal_year_start = $fiscal_year_start;
927 
928  if ($startParam) $this->startParam = $startParam;
929  if ($endParam) $this->endParam = $endParam;
930 
931  $this->calculateDateRange();
932 
933  $this->label = "";
934  if ($parent->data->hasField($field))
935  {
936  $parent->override($field, $label, $this);
937  }
938  else
939  {
940  $parent->add($this, $field);
941  }
942  $this->FieldRenderer($parent);
943 
944  // Needs to have a value so that it is included in the constraint
945  $parent->setParam($this->startParam, "to", $this->to);
946  // override default search constraint generation
947  $parent->setHandler($this->startParam, array($this, getConstraint));
948  }
949 
951  {
952  $this->startDate = $_REQUEST[$this->startParam];
953  $this->endDate = $_REQUEST[$this->endParam];
954 
955  if ($this->startDate || $this->endDate)
956  {
957  $validator = new DateValidator($this->startParam, "From");
958  $validator->required = false;
959  $this->msg = $validator->validate();
960 
961  if ($this->msg)
962  {
963  $startDate = null;
964  }
965 
966  $validator = new DateValidator($this->endParam, "To");
967  $validator->required = false;
968 
969  $this->msg = $validator->validate();
970 
971  if ($this->msg)
972  {
973  $endDate = null;
974  }
975 
976  if ($this->startDate) $this->startDate = new DateTime($this->startDate);
977  if ($this->endDate) $this->endDate = new DateTime($this->endDate);
978 
979  if ($this->startDate && $this->endDate)
980  {
981  $this->subTitle = $this->startDate->format("jS F Y")." to ".$this->endDate->format("jS F Y");
982  }
983 
984  // JDG 2/12 - remove "inclusive of last day"
985 
986  // JDG 11/2011 - Need to set this for stepping with Prev Next
987  $this->month = $this->startDate->format("m");
988  $this->year = $this->startDate->format("Y");
989  }
990 
991  $this->month = checkNumeric($_REQUEST['month']);
992  $this->year = checkNumeric($_REQUEST['year']);
993 
994  if (!$this->year) $this->year = date("Y");
995 
996  // JDG 11/2011 - need to set step mode whether we have a
997  // custom start/end date or not
998  if ($this->stepMode == "year")
999  {
1000  // calendar year stepping mode
1001  $this->setStepModeYear();
1002  }
1003  /*
1004  * If today's month for this year is less than
1005  * the fiscal year month, then show last year's
1006  * data
1007  */
1008  elseif($this->stepMode == "fiscal_year")
1009  {
1010  if(!checkNumeric($_REQUEST['year']) && $this->fiscal_year_start > date("n"))
1011  $this->year--;
1012  $this->setStepModeYear($this->fiscal_year_start);
1013  }
1014  else
1015  {
1016  // Monthly stepping mode
1017  $this->setStepModeMonth();
1018  }
1019 
1020  $this->from = $this->startDate->format("Y-m-d");
1021  $this->to = $this->endDate->format("Y-m-d");
1022  $this->calculated = true;
1023  }
1024 
1025  /*
1026  * The fiscal year in settings is stored as a string;
1027  * use this function to convert to a number.
1028  */
1029  static function convertMonthStringToNumber($month)
1030  {
1031  for($i=1;$i<=12;$i++)
1032  {
1033  if(date("F", mktime(0, 0, 0, $i, 1, 0)) == $month)
1034  {
1035  $month_number = $i;
1036  break;
1037  }
1038  }
1039  return $month_number;
1040  }
1041 
1042  function setStepModeMonth()
1043  {
1044  if (!$this->month)
1045  {
1046  $this->year = date("Y");
1047  $this->month = date("m");
1048  }
1049 
1050  $this->prevMonth = $this->month - 1;
1051  $this->prevYear = $this->year;
1052  if ($this->prevMonth == 0)
1053  {
1054  $this->prevYear--;
1055  $this->prevMonth = 12;
1056  }
1057 
1058  $this->nextMonth = $this->month + 1;
1059  $this->nextYear = $this->year;
1060  if ($this->nextMonth > 12)
1061  {
1062  $this->nextMonth = 1;
1063  $this->nextYear++;
1064  }
1065 
1066  if(!$this->startDate)
1067  $this->startDate = new DateTime("{$this->year}-{$this->month}-01T00:00:00");
1068 
1069  if(!$this->endDate)
1070  {
1071  $this->endDate = clone $this->startDate;
1072  $this->endDate->modify("+1 months");
1073  // JDG 2/12 - show last day of month
1074  $this->endDate->modify("-1 days");
1075  }
1076 
1077  if(!$this->subTitle)
1078  {
1079  $this->subTitle = $this->startDate->format("F Y");
1080  }
1081  }
1082 
1083  function setStepModeYear($startMonth = 1)
1084  {
1085  $this->month = $startMonth;
1086 
1087  $this->prevMonth = $startMonth;
1088  $this->prevYear = $this->year - 1;
1089 
1090  $this->nextMonth = $startMonth;
1091  $this->nextYear = $this->year + 1;
1092  $this->subTitle= $this->year;
1093 
1094  if(!$this->startDate)
1095  {
1096  $this->startDate = new DateTime("{$this->year}-{$this->month}-01T00:00:00");
1097  }
1098 
1099  if(!$this->endDate)
1100  {
1101  $this->endDate = clone $this->startDate;
1102  $this->endDate->modify("+1 years");
1103  $this->endDate->modify("-1 days");
1104  }
1105  }
1106 
1107  function getConstraint()
1108  {
1109  if(is_array($this->handler) && method_exists($this->handler[0], $this->handler[1]))
1110  {
1111  return call_user_func($this->handler, $this);
1112  }
1113 
1114  //AJG: Cope with date rounding
1115  $to = $this->to . " 23:59:59.999";
1116 
1117  if($this->field == $this->endDateField)
1118  {
1119  $name = $this->field;
1120  $constraint = "$name BETWEEN '{$this->from}' AND '{$to}'";
1121  }
1122  else
1123  {
1124  $constraint = "{$this->endDateField} <= '{$to}' AND {$this->field} >= '{$this->from}' ";
1125  }
1126 
1127  return $constraint;
1128  }
1129 
1130  function setHandler($handler)
1131  {
1132  $this->handler = $handler;
1133  }
1134 
1135  function renderSearchScript($field)
1136  {
1137  if (array_key_exists($field, $this->parent->hidden)) return "";
1138 
1139  $script .= <<<ENDSCRIPT
1140 
1141 <script src='/fakoli/calendar/sonjara_calendar.js' type='text/javascript'></script>
1142 <link href='/fakoli/calendar/sonjara_calendar.css' type='text/css' rel='stylesheet'></link>
1143 <script type="text/javascript">
1144 var startDateCalendar = new Calendar('startDateCalendar', '{$this->parent->id}', '{$this->startParam}');
1145 var endDateCalendar = new Calendar('endDateCalendar', '{$this->parent->id}', '{$this->endParam}');
1146 </script>
1147 
1148 ENDSCRIPT;
1149 
1150  echo $script;
1151  }
1152 
1161  function renderSearchField($field)
1162  {
1163 
1164  $this->_startField($field);
1165 
1166  if (!array_key_exists($this->startParam, $this->parent->hidden))
1167  {
1168  $this->writeFilter();
1169  }
1170  else
1171  {
1172  $this->writeDateHiddenFields();
1173  }
1174 
1175  $this->writeButtons();
1176 
1177  $this->_endField($field);
1178  }
1179 
1181  {
1182  if(!$_REQUEST["month"] || !$_REQUEST["year"])
1183  {
1184  ?>
1185  <input type="hidden" name="<?php echo $this->startParam ?>" value="<?echo $this->startDate->format("m/d/Y")?>">
1186  <input type="hidden" name="<?php echo $this->endParam ?>" value="<?echo $this->endDate->format("m/d/Y")?>">
1187  <?php
1188  }
1189  else
1190  {
1191  ?>
1192  <input type="hidden" name="year" value="<? echo $this->year ?>">
1193  <input type="hidden" name="month" value="<? echo $this->month ?>">
1194  <?php
1195  }
1196  }
1197 
1198  function writeFilter()
1199  {
1200  if (!$this->calculated) $this->calculateDateRange();
1201  $endDate = clone $this->endDate;
1202 
1203 ?>
1204 <div style="clear: both; float: left; padding-bottom: 4px">
1205  <label><?php echo $this->dateRangeLabel ?> From: </label>
1206  <input type="text" size="10" name="<?php echo $this->startParam ?>" value="<?echo $this->startDate->format("m/d/Y")?>">&nbsp;
1207  <img src='/fakoli/calendar/calendar.gif' alt='Popup Calendar' style='vertical-align: middle; border:0' onclick='startDateCalendar.toggle(this)'/>
1208  <label> To: </label><input type="text" size="10" name="<?php echo $this->endParam ?>" value="<?echo $endDate->format("m/d/Y")?>">&nbsp;
1209  <img src='/fakoli/calendar/calendar.gif' alt='Popup Calendar' style='vertical-align: middle; border:0' onclick='endDateCalendar.toggle(this)'/>
1210  &nbsp;<input type="submit" class="button" value=" Update View "/>
1211  </div>
1212 <?
1213  }
1214 
1215  function writeButtons()
1216  {
1217  if ($this->stepMode == "none") return;
1218 
1219  if (!$this->calculated) $this->calculateDateRange();
1220 
1221  $queryString = "?".$_SERVER["QUERY_STRING"];
1222 
1223  $queryString = preg_replace("/identifier=\\w+\\&?/", "", $queryString);
1224  $queryString = preg_replace("/year=\\d+\\&?/", "", $queryString);
1225  $queryString = preg_replace("/month=\\d+\\&?/", "", $queryString);
1226  $queryString = preg_replace("/startDate=\\d+\\&?/", "", $queryString);
1227  $queryString = preg_replace("/endDate=\\d+\\&?/", "", $queryString);
1228 
1229  $prevLink = appendToQueryString($queryString, "year=$this->prevYear&month=$this->prevMonth");
1230  $nextLink = appendToQueryString($queryString, "year=$this->nextYear&month=$this->nextMonth");
1231 
1232  $stepLabel = ($this->stepLabel) ? $this->stepLabel : prettify($this->stepMode);
1233 ?>
1234 <div class="<?php echo $this->cssClass ?>">
1235 <div style="float: right; z-index:2; padding-bottom: 10px;">
1236 <input type="button" class="button" value=" Next <?echo $stepLabel ?> &raquo; " onclick="go('<?echo $nextLink?>');">
1237 </div>
1238 <input type="button" class="button" value=" &laquo; Previous <?echo $stepLabel ?> " onclick="go('<?echo $prevLink?>');">
1239 </div>
1240 <?
1241  }
1242 }
1243 
1244 
1245 class ProgressiveSearchFilterFieldRenderer extends FieldRenderer
1246 {
1247  var $cssClass = 'prog_search';
1248  var $searchOptions = "";
1249  var $size = 12;
1250 
1260  function ProgressiveSearchFilterFieldRenderer(&$parent, $field, $label = "", $searchOptions)
1261  {
1262  $this->FieldRenderer($parent);
1263  if ($field && !$parent->data->hasField($field))
1264  {
1265  $parent->add($this, $field);
1266  $parent->overrides[$field]['label'] = $label;
1267  }
1268  else if($label)
1269  {
1270  $parent->override($field, $label, $this);
1271  }
1272 
1273  $this->searchOptions = $searchOptions;
1274  }
1275 
1276  function renderSearchField($field)
1277  {
1278  $this->_startField($field, $this->styles);
1279 
1280  echo "<input id='$field' type='text' name='$field'
1281  value='".$this->parent->data->get($field)."' autocomplete='off'
1282  size='{$this->size}'/>";
1283 
1284  $this->_endField($field);
1285  }
1286 
1287  function renderSearchScript($field)
1288  {
1289 ?>
1290 <script type="text/javascript">
1291 window.addEvent('domready', function()
1292 {
1293  new ProgressiveSearch('<?php echo $field ?>', <?php echo $this->searchOptions ?>);
1294 });
1295 </script>
1296  <?
1297  }
1298 
1299 
1300  function renderField($field)
1301  {
1302  $this->renderSearchField($field);
1303  }
1304 }
1305 
1306 class RelatedItemSelectFilterFieldRenderer extends RelatedItemSelectFieldRenderer
1307 {
1308  function __construct(&$form, $field, $label, $relatedClass, $constraint, $nameField, $valueField = "", $maxChars = 80)
1309  {
1310  $this->RelatedItemSelectFieldRenderer($form, $field, $label, $relatedClass, $constraint, $nameField, $valueField = "", false, true, $maxChars);
1311  }
1312 
1313 
1314  function renderSearchField($field)
1315  {
1316  $onSubmit = $this->parent->getOnSubmitFunction();
1317  $onchange = " onchange='if ({$onSubmit}(this.form)) this.form.manager.submit();'";
1318 
1319  $this->getRelatedItems();
1320 
1321  if($this->width)
1322  $style = "style='width: {$this->width}'";
1323 
1324  echo "<div>\n";
1325  $this->_printLabel($field);
1326  echo "<select name='{$field}'$onchange $style>\n";
1327  //trace("valueField: $valueField", 3);
1328 
1329  $value = $this->parent->data->get($field);
1330 
1331  foreach($this->items as $item)
1332  {
1333  $valueField = ($this->valueField != "") ? $this->valueField : $item->getPrimaryKey();
1334  $name = $this->formatName($item, $this->nameField);
1335 
1336  echo "<option value='{$item->get($valueField)}'";
1337  if ($item->get($valueField) == $value) echo " selected";
1338  echo ">".ellipsis($name, $this->max_chars)."</option>\n";
1339  }
1340  echo "</select>&nbsp;";
1341  echo "</div>";
1342  }
1343 }?>
$action
Action URL for submitting the form. Generally this can be left blank to submit back to the same page.
Definition: auto_form.inc:68
$fields
Local field cache.
Definition: auto_form.inc:111
$hidden
The hidden fields collection.
Definition: auto_form.inc:72
getRenderer($field)
Retrieves the FieldRenderer object for the specified field.
Definition: auto_form.inc:512
$method
HTTP method that will be used to submit the form.
Definition: auto_form.inc:67
Field renderer for boolean data fields.
CheckListFieldRenderer: Renders the specified list of options as a list of checkboxes from which mult...
Date Validator.
Definition: validation.inc:338
FieldRenderer is the abstract base class for all FieldRenderers.
_startField($field, $styles="")
Internal method to generate the starting HTML for the field (including the label)
renderSearchScript($field, $mode)
FieldRenderers can override this method to provide any Javascript that the control requires when bein...
_endField($field)
Internal method to generate the closing HTML for the field.
renderSearchField($field, $mode)
FieldRenderers must override this method to provide the HTML implementation of the control displayed ...
FieldRenderer($parent)
Constructor.
BooleanFilterFieldRenderer(&$parent, $field="", $label="", $default=0)
CheckListFilterFieldRenderer(&$form, $field, $label, $options, $defaults="")
_startField($field, $styles="")
Internal method to generate the starting HTML for the field (including the label)
Adds a Date Range Filter with start and end dates and Next and Previous buttons to a FilterForm.
__construct($parent, $field, $endDateField="", $stepMode="month", $fiscal_year_start=1, $startParam=null, $endParam=null)
renderSearchField($field)
If the user hides the startParm field, then don't display the calendar picker and input fields for da...
RadioButtonFieldRenderer(&$parent, $field, $label, $options)
_startField($field, $styles="")
Internal method to generate the starting HTML for the field (including the label)
setDefault($title, $value)
static formatOptions($items, $template)
FilterFieldRenderer(&$form, $field, $label="", $options=null)
Filter form adds a select field renderer to a page that allows the user to select one item from the f...
getOnSubmitFunction()
$showSubmitButton
Whether to show the submit button on a filter form.
$navigationMode
Mode used for navigating after filter changed - either "form" or "panel".
FilterForm($target, $method="GET", $action="", $id="")
getConstraint($first=true, $firstText="WHERE")
$navigationFunction
Format specifier for the default javascript navigation function to be used by fields in the FilterFor...
setParam($field, $mode, $value)
drawForm()
Write out the HTML for the search form.
$rememberFilter
Set to true to remember the chosen filter parameters and re-apply them if none are set (when returnin...
getLink($url)
renderSearchFields()
setNavigationMode($mode)
Sets the navigation mode for the FilterForm.
writeScript()
Generate any javascript required by the search form.
$submitButtonLabel
Text to show on the filter form submit button, if present.
setFilterParameters()
Default for all parameters included in filter is equal Additional search parameters can be added by c...
$formLabel
Text to display before the start of the form (such as 'Filter By')
setHandler($field, $handler)
Override an existing SearchParameter constraint clause handler or add one.
Layout for list filtering using filter_form.inc.
Field renderer for data fields that must be displayed as a list of choices.
SearchForm generates forms for searching based on a supplied DataItem.
renderOneSearchField($field)
The SearchParameters class interprets the set of input parameters for a search and generates the corr...
checkNumeric($p)
Security helper function.
Definition: functions.inc:630
trace($msg, $lvl=3, $callStack=null)
Send output to the trace log.
Definition: functions.inc:1010
option($value, $text, $sel="")
Write out an option tag, marking as selected if applicable.
Definition: functions.inc:888
ellipsis($txt, $max, $wholeWord=false)
Truncate the supplied text at the given maximum length.
Definition: functions.inc:779
appendToQueryString($qs, $params)
Appends the specified parameters to the supplied query string.
Definition: functions.inc:1594
prettify($name)
Takes a variable or field name and converts it into a human-readable version (assuming that the origi...
Definition: functions.inc:1413