CWIS Developer Documentation
SearchParameterSetEditingUI.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: SearchParameterSetEditingUI.php
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
14 {
24  public function __construct($FormFieldName, $SearchParams = NULL)
25  {
26  $this->EditFormName = $FormFieldName;
27 
28  if ($SearchParams !== NULL)
29  {
30  $this->SearchParams = $SearchParams;
31  }
32  else
33  {
34  $this->SearchParams = new SearchParameterSet();
35  }
36 
37  # get the list of fields that are allowed in searches for all schemas
38  $this->MFields = array();
39  $this->AllSchemas = MetadataSchema::GetAllSchemas();
40  foreach ($this->AllSchemas as $SCId => $Schema)
41  {
42  foreach ($Schema->GetFields(NULL, MetadataSchema::MDFORDER_ALPHABETICAL)
43  as $FId => $Field)
44  {
45  if ($Field->IncludeInAdvancedSearch() ||
46  $Field->IncludeInKeywordSearch() )
47  {
48  $this->MFields[]= $Field;
49  }
50  }
51  }
52 
53  $this->Factories = array();
54  }
55 
64  public function DisplayAsTable($TableId = NULL, $TableStyle = NULL)
65  {
66  print('<table id="'.defaulthtmlentities($TableId).'" '
67  .'class="'.defaulthtmlentities($TableStyle).'" '
68  .'style="width: 100%">');
69  $this->DisplayAsRows();
70  print('</table>');
71  }
72 
77  public function DisplayAsRows()
78  {
79 
80  $Fields = $this->FlattenSearchParams(
81  $this->SearchParams);
82 
83  # make sure the necessary javascript is required
84  $GLOBALS["AF"]->RequireUIFile("jquery-ui.js");
85  $GLOBALS["AF"]->RequireUIFile("CW-QuickSearch.js");
86  $GLOBALS["AF"]->RequireUIFile("SearchParameterSetEditingUI.js");
87 
88  # note that all of the fields we create for these rows will be named
89  # $this->EditFormName.'[]' , combining them all into an array of results per
90  # http://php.net/manual/en/faq.html.php#faq.html.arrays
91 
92  # css classes required by our javascript are logic_row
93  # field-row, and field-value-edit
94 
95  $Depth = 0;
96 
97  foreach ($Fields as $FieldRow)
98  {
99  if (is_string($FieldRow) && $FieldRow == "(")
100  {
101  $Depth++;
102  print('<tr><td colspan=2 style="padding-left: 2em;">'
103  .'<input type="hidden" name="'.$this->EditFormName.'[]" '
104  .'value="X-BEGIN-SUBGROUP-X"/>'
105  .'<table class="cw-speui-subgroup">');
106  }
107  elseif (is_string($FieldRow) && $FieldRow == ")")
108  {
109  $Depth--;
110  $this->PrintTemplateRow();
111  print('<input type="hidden" name="'.$this->EditFormName.'[]" '
112  .'value="X-END-SUBGROUP-X"/></table></td></tr>');
113  }
114  elseif (is_array($FieldRow) && isset($FieldRow["Logic"]))
115  {
116  print('<tr class="logic_row '.$this->EditFormName.'">'
117  .'<td colspan="3">'
118  .($Depth==0?'Top-Level Logic: ':'Subgroup with '));
119 
120  $ListName = $this->EditFormName."[]";
121  $Options = array("AND"=>"AND", "OR"=>"OR");
122  $SelectedValue = $FieldRow["Logic"];
123 
124  $OptList = new HtmlOptionList($ListName, $Options, $SelectedValue);
125  $OptList->ClassForList("logic");
126  $OptList->PrintHtml();
127 
128  print (($Depth>0?' Logic':'').'</td></tr>');
129  }
130  elseif (is_array($FieldRow) && isset($FieldRow["FieldId"]) )
131  {
132  $FieldId = $FieldRow["FieldId"];
133  $Values = $FieldRow["Values"];
134  foreach ($Values as $CurVal)
135  {
136  print('<tr class="field-row '.$this->EditFormName.'""
137  ." style="white-space: nowrap;">'
138  ."<td><span class=\"cw-button cw-button-elegant "
139  ."cw-button-constrained cw-speui-delete\">X</span>"
140  ."</td><td>");
141 
142  # for selectable fields, we need to generate all the
143  # html elements that we might need and then depend on
144  # javascript to display only those that are relevant
145 
146  # each field will have four elements
147 
148  # 1. a field selector
149  $this->PrintFieldSelector($FieldId);
150 
151  # 2. a value selector (for option and flag values)
152  $this->PrintValueSelector($FieldId, $CurVal);
153 
154  $SearchText = (StdLib::strpos($CurVal, "=")===0) ?
155  StdLib::substr($CurVal, 1) : $CurVal;
156 
157  # 3. a text entry
158  print('<input type="text" class="field-value-edit" '
159  .'name="'.$this->EditFormName.'[]" '
160  .'placeholder="(search terms)" '
161  .'value="'.defaulthtmlentities($SearchText).'">');
162 
163  # 4. an ajax search box
164  $this->PrintQuicksearch($FieldId, $SearchText);
165 
166  print("</td></tr>");
167  }
168  }
169  }
170 
171  # add a template row, used for adding new fields
172  $this->PrintTemplateRow();
173  }
174 
180  public function GetValuesFromFormData()
181  {
182  if (!isset($_POST[$this->EditFormName]))
183  {
184  $Result = new SearchParameterSet();
185  }
186  else
187  {
188  # set up our result
189  $GroupStack = array();
190  array_push($GroupStack, new SearchParameterSet() );
191 
192  # extract the array of data associated with our EditFormName
193  $FormData = $_POST[$this->EditFormName];
194 
195  # extract and set the search logic, which is always the first
196  # element in the HTML that we generate
197  $Logic = array_shift($FormData);
198  end($GroupStack)->Logic($Logic);
199 
200  while (count($FormData))
201  {
202  # first element of each row is a field id
203  $FieldId = array_shift($FormData);
204 
205  if ($FieldId == "X-BEGIN-SUBGROUP-X")
206  {
207  # add a new subgroup to our stack of subgroups
208  array_push($GroupStack, new SearchParameterSet());
209  # extract and set the search logic
210  $Logic = array_shift($FormData);
211  end($GroupStack)->Logic($Logic);
212  }
213  elseif ($FieldId == "X-END-SUBGROUP-X")
214  {
215  $Subgroup = array_pop($GroupStack);
216  end($GroupStack)->AddSet($Subgroup);
217  }
218  else
219  {
220  # for selectable fields, we'll have all possible
221  # elements and will need to grab the correct ones for
222  # the currently selected field
223  $SelectVal = array_shift($FormData);
224  $TextVal = array_shift($FormData);
225  $SearchVal = array_shift($FormData);
226 
227  if ($FieldId == "X-KEYWORD-X")
228  {
229  $Val = $TextVal;
230  $Field = NULL;
231 
232  if (strlen($TextVal)==0)
233  {
234  continue;
235  }
236  }
237  else
238  {
239  $Field = new MetadataField($FieldId);
240 
241  # make sure we have factories for field types that need them
242  switch ($Field->Type())
243  {
247  if (!isset($this->Factories[$FieldId]))
248  {
249  $this->Factories[$FieldId] = $Field->GetFactory();
250  }
251  break;
252 
253  default:
254  break;
255  }
256 
257  # verify that we actually have a value for our selected field
258  switch ($Field->Type())
259  {
267  # if we have no value for this field, skip displaying it
268  if (strlen($TextVal)==0)
269  {
270  continue 2;
271 
272  }
273  break;
274 
277  # if we have no value for this field, skip displaying it
278  if (strlen($SearchVal)==0)
279  {
280  continue 2;
281  }
282  break;
283 
284  # no need to check the types where there's
285  # a SelectVal, as that cannot be left empty
286  default:
287  break;
288  }
289 
290  # extract the value for our field
291  switch ($Field->Type())
292  {
299  $Val = $TextVal;
300  break;
301 
303  $Val = "=".$TextVal;
304  break;
305 
307  $Item = $this->Factories[$FieldId]->GetItem(
308  $SearchVal);
309 
310  # for tree fields, use the same 'is X
311  # or a child of X' construction that we
312  # use when generating search facets
313  $Val = new SearchParameterSet();
314  $Val->Logic("OR");
315  $Val->AddParameter(array(
316  "=".$Item->Name(),
317  "^".$Item->Name()." -- "), $Field);
318  break;
319 
321  $Item = $this->Factories[$FieldId]->GetItem(
322  $SearchVal);
323  $Val = "=".$Item->Name();
324  break;
325 
327  list($InputId, $InputVal) = explode("-", $SelectVal, 2);
328  $Item = $this->Factories[$FieldId]->GetItem(
329  $InputVal);
330  $Val = "=".$Item->Name();
331  break;
332 
334  list($InputId, $InputVal) = explode("-", $SelectVal, 2);
335  $Val = "=".$InputVal;
336  break;
337 
338  default:
339  throw new Exception("Unsupported field type");
340  }
341  }
342 
343  # add our value to the search parameters
344  if ($Val instanceof SearchParameterSet)
345  {
346  end($GroupStack)->AddSet($Val);
347  }
348  else
349  {
350  end($GroupStack)->AddParameter($Val, $Field);
351  }
352  }
353  }
354 
355  $Result = array_pop($GroupStack);
356  }
357 
358  $this->SearchParams = $Result;
359  return $Result;
360  }
361 
367  public function SearchParameters($SearchParams = NULL)
368  {
369  if ($SearchParams !== NULL)
370  {
371  $this->SearchParams = clone $SearchParams;
372  }
373 
374  return clone $this->SearchParams;
375  }
376 
387  public function MaxFieldLabelLength($NewValue = NULL)
388  {
389  if (!is_null($NewValue))
390  {
391  $this->MaxFieldLabelLength = $NewValue;
392  }
393  return $this->MaxFieldLabelLength;
394  }
395 
406  public function MaxValueLabelLength($NewValue = NULL)
407  {
408  if (!is_null($NewValue))
409  {
410  $this->MaxValueLabelLength = $NewValue;
411  }
412  return $this->MaxValueLabelLength;
413  }
414 
415 
416  # ---- PRIVATE INTERFACE -------------------------------------------------
417 
418  private $EditFormName;
419  private $SearchParams;
420  private $MFields;
421  private $AllSchemas;
422  private $Factories;
423  private $MaxFieldLabelLength = 0;
424  private $MaxValueLabelLength = 0;
425 
437  private function FlattenSearchParams($SearchParams)
438  {
439  $Result = array();
440 
441  $Result[]= array(
442  "Logic" => $SearchParams->Logic() );
443 
444  $SearchStrings = $SearchParams->GetSearchStrings();
445  foreach ($SearchStrings as $FieldId => $Values)
446  {
447  $Result[]= array(
448  "FieldId" => $FieldId,
449  "Values" => $Values);
450  }
451 
452  $KeywordStrings = $SearchParams->GetKeywordSearchStrings();
453  if (count($KeywordStrings))
454  {
455  $Result[]= array(
456  "FieldId" => "X-KEYWORD-X",
457  "Values" => $KeywordStrings);
458  }
459 
460  $Subgroups = $SearchParams->GetSubgroups();
461  if (count($Subgroups))
462  {
463  foreach ($Subgroups as $Subgroup)
464  {
465  $Result[]= "(";
466  $SubgroupItems = $this->FlattenSearchParams($Subgroup);
467  foreach ($SubgroupItems as $Item)
468  {
469  $Result[] = $Item;
470  }
471  $Result[]= ")";
472  }
473  }
474 
475  return $Result;
476  }
477 
482  private function PrintFieldSelector($FieldId)
483  {
484  $ListName = $this->EditFormName."[]";
485  $SelectedValue = array();
486 
487  # "Keyword" option is always here
488  $Options["X-KEYWORD-X"] = "Keyword";
489  $OptionClass["X-KEYWORD-X"] = "field-type-keyword";
490  if ($FieldId == "X-KEYWORD-X")
491  {
492  $SelectedValue[] = "X-KEYWORD-X";
493  }
494 
495  # prepare options for print
496  foreach ($this->MFields as $MField)
497  {
498  $TypeName = defaulthtmlentities(
499  str_replace(' ', '', strtolower($MField->TypeAsName())));
500 
501  if (!$MField->Optional())
502  {
503  $TypeName .= " required";
504  }
505 
506  $FieldName = $MField->Name();
507  if ($MField->SchemaId() != MetadataSchema::SCHEMAID_DEFAULT)
508  {
509  $FieldName = $this->AllSchemas[$MField->SchemaId()]->Name()
510  .": ".$FieldName;
511  }
512 
513  $Options[$MField->Id()] = defaulthtmlentities($FieldName);
514  $OptionClass[$MField->Id()] = "field-type-".$TypeName;
515 
516  if ($FieldId == $MField->Id())
517  {
518  $SelectedValue[] = $MField->Id();
519  }
520  }
521 
522  # instantiate option list and print
523  $OptList = new HtmlOptionList($ListName, $Options, $SelectedValue);
524  $OptList->ClassForList("field-subject");
525  $OptList->ClassForOptions($OptionClass);
526  $OptList->MaxLabelLength($this->MaxFieldLabelLength);
527  $OptList->PrintHtml();
528  }
529 
530 
536  private function PrintValueSelector($FieldId, $CurVal)
537  {
538  # parameters of the option list
539  $ListName = $this->EditFormName."[]";
540  $Options = array();
541  $OptionClass = array();
542  $SelectedValue = array();
543 
544  # prepare options for print
545  foreach ($this->MFields as $MField)
546  {
547  if ($MField->Type() == MetadataSchema::MDFTYPE_FLAG ||
548  $MField->Type() == MetadataSchema::MDFTYPE_OPTION)
549  {
550  foreach ($MField->GetPossibleValues() as $Id => $Val)
551  {
552  $IsSelected = FALSE;
553  $Key = $MField->Id()."-".$Id;
554 
555  if ($MField->Id() == $FieldId)
556  {
557  $TgtVal = ($MField->Type() == MetadataSchema::MDFTYPE_FLAG) ?
558  "=".$Id : "=".$Val ;
559  $IsSelected = ($CurVal == $TgtVal) ? TRUE : FALSE ;
560  }
561 
562  $Options[$Key] = defaulthtmlentities($Val);
563  $OptionClass[$Key] = "field-id-".$MField->Id();
564 
565  if ($IsSelected)
566  {
567  $SelectedValue[] = $Key;
568  }
569  }
570  }
571  }
572 
573  # instantiate an option list and print
574  $OptList = new HtmlOptionList($ListName, $Options, $SelectedValue);
575  $OptList->ClassForList("field-value-select");
576  $OptList->ClassForOptions($OptionClass);
577  $OptList->MaxLabelLength($this->MaxValueLabelLength);
578  $OptList->PrintHtml();
579  }
580 
586  private function PrintQuicksearch($FieldId, $CurVal)
587  {
588  if ($FieldId !== NULL && $FieldId != "X-KEYWORD-X")
589  {
590  if (!isset($this->Factories[$FieldId]))
591  {
592  $Field = new MetadataField($FieldId);
593  $Factory = $Field->GetFactory();
594 
595  $this->Factories[$FieldId] =
596  ($Factory !== NULL) ? $Factory : FALSE;
597  }
598 
599  $ItemId = ($this->Factories[$FieldId] !== FALSE) ?
600  $this->Factories[$FieldId]->GetItemIdByName($CurVal) : "" ;
601  }
602  else
603  {
604  $ItemId = "";
605  }
606 
607  # field-value-qs css class required by our javascript.
608  # various cw-quicksearch classes required by the quicksearch javascript
609  # and ui-front class required by jquery-ui, used by qs js
610  print('<div class="field-value-qs cw-quicksearch '
611  . 'cw-quicksearch-template">'
612  .'<input class="cw-quicksearch-display '
613  .'cw-resourceeditor-metadatafield" '
614  .'placeholder="(enter some text to begin searching)" '
615  .'value="'.defaulthtmlentities($CurVal).'" />'
616  .'<input name="'.$this->EditFormName.'[]" '
617  .'class="cw-quicksearch-value" type="hidden" '
618  .'value="'.defaulthtmlentities($ItemId).'" />'
619  .'<div style="display: none;" '
620  .'class="cw-quicksearch-menu">'
621  .'<div class="cw-quicksearch-message ui-front"></div>'
622  .'</div></div>');
623  }
624 
628  private function PrintTemplateRow()
629  {
630  # field-row, template-row, field-value-edit, cw-speui-add, and
631  # cw-speui-add-subgroup css classes required by our javascript
632  print(
633  "<tr class=\"field-row template-row ".$this->EditFormName."\""
634  ." style=\"white-space: nowrap;\">"
635  ."<td>"
636  ."<span class=\"cw-button cw-button-elegant cw-button-constrained "
637  ."cw-speui-delete\">X</span>"
638  ."</td><td>");
639  $this->PrintFieldSelector(NULL);
640  $this->PrintValueSelector(NULL, "");
641  print("<input type=\"text\" class=\"field-value-edit\" "
642  ."name=\"".$this->EditFormName."[]\" placeholder=\"(search terms)\" "
643  ."value=\"\">");
644  $this->PrintQuicksearch(NULL, "");
645  print("</td></tr>");
646  print("<tr><td colspan=2>"
647  ."<span class=\"cw-button cw-button-elegant cw-button-constrained "
648  ."cw-speui-add\">Add Field</span>"
649  ."<span class=\"cw-button cw-button-elegant cw-button-constrained "
650  ."cw-speui-add-subgroup\">Add Subgroup</span>"
651  ."</td></tr>");
652  }
653 }
GetValuesFromFormData()
Extract values from a dynamics field edit/modification form.
DisplayAsRows()
Display the table rows for the editing form, without the surrounding.
SearchParameters($SearchParams=NULL)
Get/Set search parameters.
const MDFORDER_ALPHABETICAL
Set of parameters used to perform a search.
DisplayAsTable($TableId=NULL, $TableStyle=NULL)
Display editing form elements enclosed in a.
MaxValueLabelLength($NewValue=NULL)
Get/set the max number of characters a label of a value option list will be displayed.
static strpos()
Multibyte-aware (if supported in PHP) version of strpos().
Definition: StdLib.php:529
__construct($FormFieldName, $SearchParams=NULL)
Create a UI for specifing edits to SearchParameterSets.
const MDFTYPE_CONTROLLEDNAME
static substr()
Multibyte-aware (if supported in PHP) version of substr().
Definition: StdLib.php:520
MaxFieldLabelLength($NewValue=NULL)
Get/set the max number of characters a label of a field option list will be displayed.
Object representing a locally-defined type of metadata field.
static GetAllSchemas()
Get all existing metadata schemas.
Convenience class for generating an HTML select/option form element.
Class to create a user interface for editing SearchParameterSets.