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  # iterate over the search parameter set, creating a
29  # flattened representation of it that is easier for printing
30  if ($SearchParams !== NULL)
31  {
32  $this->SearchParams = SPTSearchEngine::ConvertToDisplayParameters(
33  $SearchParams);
34  }
35  else
36  {
37  $this->SearchParams = new SearchParameterSet();
38  }
39 
40  # get the list of fields that are allowed in searches for all schemas
41  $this->MFields = array();
42  $this->AllSchemas = MetadataSchema::GetAllSchemas();
43  foreach ($this->AllSchemas as $SCId => $Schema)
44  {
45  foreach ($Schema->GetFields(NULL, MetadataSchema::MDFORDER_ALPHABETICAL)
46  as $FId => $Field)
47  {
48  if ($Field->IncludeInAdvancedSearch() ||
49  $Field->IncludeInKeywordSearch() )
50  {
51  $this->MFields[]= $Field;
52  }
53  }
54  }
55 
56  $this->Factories = array();
57  }
58 
67  public function DisplayAsTable($TableId = NULL, $TableStyle = NULL)
68  {
69  print('<table id="'.defaulthtmlentities($TableId).'" '
70  .'class="'.defaulthtmlentities($TableStyle).'" '
71  .'style="width: 100%">');
72  $this->DisplayAsRows();
73  print('</table>');
74  }
75 
80  public function DisplayAsRows()
81  {
82 
83  $Fields = $this->FlattenSearchParams(
84  $this->SearchParams);
85 
86  # make sure the necessary javascript is required
87  $GLOBALS["AF"]->RequireUIFile("jquery-ui.js");
88  $GLOBALS["AF"]->RequireUIFile("CW-QuickSearch.js");
89  $GLOBALS["AF"]->RequireUIFile("SearchParameterSetEditingUI.js");
90 
91  # note that all of the fields we create for these rows will be named
92  # $this->EditFormName.'[]' , combining them all into an array of results per
93  # http://php.net/manual/en/faq.html.php#faq.html.arrays
94 
95  # css classes required by our javascript are logic_row
96  # field-row, and field-value-edit
97 
98  $Depth = 0;
99 
100  foreach ($Fields as $FieldRow)
101  {
102  if (is_string($FieldRow) && $FieldRow == "(")
103  {
104  $Depth++;
105  print('<tr><td colspan=2 style="padding-left: 2em;">'
106  .'<input type="hidden" name="'.$this->EditFormName.'[]" '
107  .'value="X-BEGIN-SUBGROUP-X"/>'
108  .'<table class="cw-speui-subgroup">');
109  }
110  elseif (is_string($FieldRow) && $FieldRow == ")")
111  {
112  $Depth--;
113  $this->PrintTemplateRow();
114  print('<input type="hidden" name="'.$this->EditFormName.'[]" '
115  .'value="X-END-SUBGROUP-X"/></table></td></tr>');
116  }
117  elseif (is_array($FieldRow) && isset($FieldRow["Logic"]))
118  {
119  print('<tr class="logic_row '.$this->EditFormName.'">'
120  .'<td colspan="3">'.($Depth==0?'Top-Level Logic: ':'Subgroup with ')
121  .'<select name="'.$this->EditFormName.'[]" '
122  .'class="logic">'
123  .'<option value="AND" '
124  .($FieldRow["Logic"]=="AND" ? "selected ":"")
125  .'>AND</option>'
126  .'<option value="OR" '
127  .($FieldRow["Logic"]=="OR" ? "selected ":"")
128  .'>OR</option>'
129  .'</select>'.($Depth>0?' Logic':'').'</td></tr>');
130  }
131  elseif (is_array($FieldRow) && isset($FieldRow["FieldId"]) )
132  {
133  $FieldId = $FieldRow["FieldId"];
134  $Values = $FieldRow["Values"];
135  foreach ($Values as $CurVal)
136  {
137  print('<tr class="field-row '.$this->EditFormName.'""
138  ." style="white-space: nowrap;">'
139  ."<td><span class=\"cw-button cw-button-elegant "
140  ."cw-button-constrained cw-speui-delete\">X</span>"
141  ."</td><td>");
142 
143  # for selectable fields, we need to generate all the
144  # html elements that we might need and then depend on
145  # javascript to display only those that are relevant
146 
147  # each field will have four elements
148 
149  # 1. a field selector
150  $this->PrintFieldSelector($FieldId);
151 
152  # 2. a value selector (for option and flag values)
153  $this->PrintValueSelector($FieldId, $CurVal);
154 
155  $SearchText = (mb_strpos($CurVal, "=")===0) ?
156  mb_substr($CurVal, 1) : $CurVal;
157 
158  # 3. a text entry
159  print('<input type="text" class="field-value-edit" '
160  .'name="'.$this->EditFormName.'[]" '
161  .'placeholder="(search terms)" '
162  .'value="'.defaulthtmlentities($SearchText).'">');
163 
164  # 4. an ajax search box
165  $this->PrintQuicksearch($FieldId, $SearchText);
166 
167  print("</td></tr>");
168  }
169  }
170  }
171 
172  # add a template row, used for adding new fields
173  $this->PrintTemplateRow();
174  }
175 
181  public function GetValuesFromFormData()
182  {
183  if (!isset($_POST[$this->EditFormName]))
184  {
185  $Result = new SearchParameterSet();
186  }
187  else
188  {
189  # set up our result
190  $GroupStack = array();
191  array_push($GroupStack, new SearchParameterSet() );
192 
193  # extract the array of data associated with our EditFormName
194  $FormData = $_POST[$this->EditFormName];
195 
196  # extract and set the search logic, which is always the first
197  # element in the HTML that we generate
198  $Logic = array_shift($FormData);
199  end($GroupStack)->Logic($Logic);
200 
201  while (count($FormData))
202  {
203  # first element of each row is a field id
204  $FieldId = array_shift($FormData);
205 
206  if ($FieldId == "X-BEGIN-SUBGROUP-X")
207  {
208  # add a new subgroup to our stack of subgroups
209  array_push($GroupStack, new SearchParameterSet());
210  # extract and set the search logic
211  $Logic = array_shift($FormData);
212  end($GroupStack)->Logic($Logic);
213  }
214  elseif ($FieldId == "X-END-SUBGROUP-X")
215  {
216  $Subgroup = array_pop($GroupStack);
217  end($GroupStack)->AddSet($Subgroup);
218  }
219  else
220  {
221  # for selectable fields, we'll have all possible
222  # elements and will need to grab the correct ones for
223  # the currently selected field
224  $SelectVal = array_shift($FormData);
225  $TextVal = array_shift($FormData);
226  $SearchVal = array_shift($FormData);
227 
228  if ($FieldId == "X-KEYWORD-X")
229  {
230  $Val = $TextVal;
231  $Field = NULL;
232 
233  if (strlen($TextVal)==0)
234  {
235  continue;
236  }
237  }
238  else
239  {
240  $Field = new MetadataField($FieldId);
241 
242  # make sure we have factories for field types that need them
243  switch ($Field->Type())
244  {
248  if (!isset($this->Factories[$FieldId]))
249  {
250  $this->Factories[$FieldId] = $Field->GetFactory();
251  }
252  break;
253 
254  default:
255  break;
256  }
257 
258  # verify that we actually have a value for our selected field
259  switch ($Field->Type())
260  {
268  # if we have no value for this field, skip displaying it
269  if (strlen($TextVal)==0)
270  {
271  continue 2;
272 
273  }
274  break;
275 
278  # if we have no value for this field, skip displaying it
279  if (strlen($SearchVal)==0)
280  {
281  continue 2;
282  }
283  break;
284 
285  # no need to check the types where there's
286  # a SelectVal, as that cannot be left empty
287  default:
288  break;
289  }
290 
291  # extract the value for our field
292  switch ($Field->Type())
293  {
300  $Val = $TextVal;
301  break;
302 
304  $Val = "=".$TextVal;
305  break;
306 
308  $Item = $this->Factories[$FieldId]->GetItem(
309  $SearchVal);
310 
311  # for tree fields, use the same 'is X
312  # or a child of X' construction that we
313  # use when generating search facets
314  $Val = new SearchParameterSet();
315  $Val->Logic("OR");
316  $Val->AddParameter(array(
317  "=".$Item->Name(),
318  "^".$Item->Name()." -- "), $Field);
319  break;
320 
322  $Item = $this->Factories[$FieldId]->GetItem(
323  $SearchVal);
324  $Val = "=".$Item->Name();
325  break;
326 
328  $Item = $this->Factories[$FieldId]->GetItem(
329  $SelectVal);
330  $Val = "=".$Item->Name();
331  break;
332 
334  $Val = $SelectVal;
335  break;
336 
337  default:
338  throw new Exception("Unsupported field type");
339  }
340  }
341 
342  # add our value to the search parameters
343  if ($Val instanceof SearchParameterSet)
344  {
345  end($GroupStack)->AddSet($Val);
346  }
347  else
348  {
349  end($GroupStack)->AddParameter($Val, $Field);
350  }
351  }
352  }
353 
354  $Result = array_pop($GroupStack);
355  }
356 
357  # set internal search params to the "Display" version, as these
358  # are the ones we shall use for display
359  $this->SearchParams = SPTSearchEngine::ConvertToDisplayParameters($Result);
360  return $Result;
361  }
362 
368  public function SearchParameters($SearchParams = NULL)
369  {
370  if ($SearchParams !== NULL)
371  {
372  $this->SearchParams = clone $SearchParams;
373  }
374 
375  return clone $this->SearchParams;
376  }
377 
378  # ---- PRIVATE INTERFACE -------------------------------------------------
379 
380  private $EditFormName;
381  private $SearchParams;
382  private $MFields;
383  private $AllSchemas;
384  private $Factories;
385 
397  private function FlattenSearchParams($SearchParams)
398  {
399  $Result = array();
400 
401  $Result[]= array(
402  "Logic" => $SearchParams->Logic() );
403 
404  $SearchStrings = $SearchParams->GetSearchStrings();
405  foreach ($SearchStrings as $FieldId => $Values)
406  {
407  $Result[]= array(
408  "FieldId" => $FieldId,
409  "Values" => $Values);
410  }
411 
412  $KeywordStrings = $SearchParams->GetKeywordSearchStrings();
413  if (count($KeywordStrings))
414  {
415  $Result[]= array(
416  "FieldId" => "X-KEYWORD-X",
417  "Values" => $KeywordStrings);
418  }
419 
420  $Subgroups = $SearchParams->GetSubgroups();
421  if (count($Subgroups))
422  {
423  foreach ($Subgroups as $Subgroup)
424  {
425  $Result[]= "(";
426  $SubgroupItems = $this->FlattenSearchParams($Subgroup);
427  foreach ($SubgroupItems as $Item)
428  {
429  $Result[] = $Item;
430  }
431  $Result[]= ")";
432  }
433  }
434 
435  return $Result;
436  }
437 
442  private function PrintFieldSelector($FieldId)
443  {
444  # field-subject css class required by our javascript
445  print('<select name="'.$this->EditFormName.'[]" '
446  .'class="field-subject">'
447  .'<option class="field-type-keyword" '
448  .($FieldId == "X-KEYWORD-X" ? "selected ":"")
449  .'value="X-KEYWORD-X">Keyword</option>');
450 
451  foreach ($this->MFields as $MField)
452  {
453  $TypeName = defaulthtmlentities(
454  str_replace(' ', '', strtolower($MField->TypeAsName())));
455 
456  if (!$MField->Optional())
457  {
458  $TypeName .= " required";
459  }
460 
461  $FieldName = $MField->Name();
462  if ($MField->SchemaId() != MetadataSchema::SCHEMAID_DEFAULT)
463  {
464  $FieldName = $this->AllSchemas[$MField->SchemaId()]->Name()
465  .": ".$FieldName;
466  }
467 
468  print('<option class="field-type-'.$TypeName.'" '
469  .($FieldId == $MField->Id() ? "selected ":"")
470  .'value="'.$MField->Id().'">'
471  .defaulthtmlentities($FieldName).'</option>');
472  }
473  print('</select>');
474  }
475 
476 
482  private function PrintValueSelector($FieldId, $CurVal)
483  {
484  # field-value-select class required by our javascript
485  print('<select name="'.$this->EditFormName.'[]" '
486  .'class="field-value-select">');
487  foreach ($this->MFields as $MField)
488  {
489  if ($MField->Type() == MetadataSchema::MDFTYPE_FLAG ||
490  $MField->Type() == MetadataSchema::MDFTYPE_OPTION)
491  {
492  foreach ($MField->GetPossibleValues() as $Id => $Val)
493  {
494  $IsSelected = $MField->Id() == $FieldId &&
495  strlen($CurVal) > 1 && $CurVal[0] == "=" &&
496  substr($CurVal, 1) == $Val ;
497 
498  print('<option value="'.$Id.'" '
499  .'class="field-id-'.$MField->Id().'" '
500  .($IsSelected ? " selected" : "")
501  .'>'.defaulthtmlentities($Val)
502  .'</option>'."\n");
503  }
504  }
505  }
506  print('</select>');
507  }
508 
514  private function PrintQuicksearch($FieldId, $CurVal)
515  {
516  if ($FieldId !== NULL && $FieldId != "X-KEYWORD-X")
517  {
518  if (!isset($this->Factories[$FieldId]))
519  {
520  $Field = new MetadataField($FieldId);
521  $Factory = $Field->GetFactory();
522 
523  $this->Factories[$FieldId] =
524  ($Factory !== NULL) ? $Factory : FALSE;
525  }
526 
527  $ItemId = ($this->Factories[$FieldId] !== FALSE) ?
528  $this->Factories[$FieldId]->GetItemIdByName($CurVal) : "" ;
529  }
530  else
531  {
532  $ItemId = "";
533  }
534 
535  # field-value-qs css class required by our javascript.
536  # various cw-quicksearch classes required by the quicksearch javascript
537  # and ui-front class required by jquery-ui, used by qs js
538  print('<div class="field-value-qs cw-quicksearch '
539  . 'cw-quicksearch-template">'
540  .'<input class="cw-quicksearch-display '
541  .'cw-resourceeditor-metadatafield" '
542  .'placeholder="(enter some text to begin searching)" '
543  .'value="'.defaulthtmlentities($CurVal).'" />'
544  .'<input name="'.$this->EditFormName.'[]" '
545  .'class="cw-quicksearch-value" type="hidden" '
546  .'value="'.defaulthtmlentities($ItemId).'" />'
547  .'<div style="display: none;" '
548  .'class="cw-quicksearch-menu">'
549  .'<div class="cw-quicksearch-message ui-front"></div>'
550  .'</div></div>');
551  }
552 
556  private function PrintTemplateRow()
557  {
558  # field-row, template-row, field-value-edit, cw-speui-add, and
559  # cw-speui-add-subgroup css classes required by our javascript
560  print(
561  "<tr class=\"field-row template-row ".$this->EditFormName."\""
562  ." style=\"white-space: nowrap;\">"
563  ."<td>"
564  ."<span class=\"cw-button cw-button-elegant cw-button-constrained "
565  ."cw-speui-delete\">X</span>"
566  ."</td><td>");
567  $this->PrintFieldSelector(NULL);
568  $this->PrintValueSelector(NULL, "");
569  print("<input type=\"text\" class=\"field-value-edit\" "
570  ."name=\"".$this->EditFormName."[]\" placeholder=\"(search terms)\" "
571  ."value=\"\">");
572  $this->PrintQuicksearch(NULL, "");
573  print("</td></tr>");
574  print("<tr><td colspan=2>"
575  ."<span class=\"cw-button cw-button-elegant cw-button-constrained "
576  ."cw-speui-add\">Add Field</span>"
577  ."<span class=\"cw-button cw-button-elegant cw-button-constrained "
578  ."cw-speui-add-subgroup\">Add Subgroup</span>"
579  ."</td></tr>");
580  }
581 }
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.
__construct($FormFieldName, $SearchParams=NULL)
Create a UI for specifing edits to SearchParameterSets.
const MDFTYPE_CONTROLLEDNAME
Object representing a locally-defined type of metadata field.
static ConvertToDisplayParameters($SearchParams)
Get a simplified SearchParameterSet for display purposes.
static GetAllSchemas()
Get all existing metadata schemas.
Class to create a user interface for editing SearchParameterSets.