3 # FILE: FieldEditingUI.php 5 # Part of the Collection Workflow Integration System (CWIS) 6 # Copyright 2014 Edward Almasy and Internet Scout Research Group 7 # http://scout.wisc.edu/cwis/ 12 # constants to define the operators supported by the editing UI 33 $this->EditFormName = $EditFormName;
36 $this->Fields = array();
38 $this->AllowedFieldTypes =
63 $CurrentOperator = NULL,
64 $AllowRemoval = FALSE)
66 # if a field name was passed in, convert it to a field id 67 if (!is_numeric($FieldNameOrId))
69 $FieldNameOrId = $this->Schema->GetFieldByName($FieldNameOrId)->Id();
72 $this->Fields[]= array(
74 "FieldId" => $FieldNameOrId,
75 "CurrentValue" => $CurrentValue,
76 "CurrentOperator" => $CurrentOperator,
77 "AllowRemoval" => $AllowRemoval );
95 $FieldTypesOrIds = NULL,
96 $CurrentFieldId = NULL,
98 $CurrentOperator = NULL,
101 $Options = $this->TypesOrIdsToFieldList($FieldTypesOrIds);
103 if (count($Options)>0)
105 $this->Fields[]= array(
106 "Type" =>
"Selectable",
107 "SelectOptions" => $Options,
108 "CurrentValue" => $CurrentValue,
109 "CurrentOperator" => $CurrentOperator,
110 "AllowRemoval" => $AllowRemoval );
125 $Options = $this->TypesOrIdsToFieldList($FieldTypesOrIds);
127 if (count($Options)>0)
129 $this->Fields[]= array(
130 "Type" =>
"AddButton",
131 "SelectOptions" => $Options,
133 "CurrentOperator" => NULL,
134 "CurrentValue" => NULL,
135 "AllowRemoval" => TRUE );
149 print(
'<table id="'.defaulthtmlentities($TableId).
'" ' 150 .
'class="'.defaulthtmlentities($TableStyle).
'">');
161 # make sure the necessary javascript is required 162 $GLOBALS[
"AF"]->REquireUIFile(
"jquery-ui.js");
163 $GLOBALS[
"AF"]->RequireUIFile(
"CW-QuickSearch.js");
164 $GLOBALS[
"AF"]->RequireUIFile(
"FieldEditingUI.js");
166 # get a list of the fields examined in this chunk of UI, to 167 # use when constructing the value selector 168 $FieldsExamined = array();
169 foreach ($this->Fields as $FieldRow)
171 if ($FieldRow[
"Type"] ==
"Regular")
173 $FieldsExamined[]= $FieldRow[
"FieldId"];
177 $FieldsExamined = array_merge(
179 $FieldRow[
"SelectOptions"]);
182 $FieldsExamined = array_unique($FieldsExamined);
184 # iterate over each field adding edit rows for all of them 186 print(
'<tr class="cw-feui-empty '.$this->EditFormName.
'" '.
187 'data-formname="'.$this->EditFormName.
'"><td colspan="4">'.
188 '<i>No fields selected for editing</i></td></tr>');
190 # note that all of the fields we create for these rows will be named 191 # $this->EditFormName.'[]' , combining them all into an array of results per 192 # http://php.net/manual/en/faq.html.php#faq.html.arrays 193 foreach ($this->Fields as $FieldRow)
195 $CurOp = $FieldRow[
"CurrentOperator"];
196 $CurVal = $FieldRow[
"CurrentValue"];
197 $AllowRemoval = $FieldRow[
"AllowRemoval"];
199 print(
'<tr class="field_row '.$this->EditFormName
200 .($FieldRow[
"Type"]==
"AddButton" ?
' template_row':
'').
'">');
203 if ($FieldRow[
"AllowRemoval"])
205 print(
"<span class=\"cw-button cw-button-elegant cw-button-constrained " 206 .
"cw-feui-delete\">X</span>");
210 if ($FieldRow[
"Type"] ==
"Regular")
214 # for fields that cannot be selected, we already know 215 # the type and can print field-specific elements 216 # (operators, etc) rather than relying on js to clean 218 $TypeName = defaulthtmlentities(
219 str_replace(
' ',
'', strtolower($Field->TypeAsName())));
221 $Field->AllowMultiple() )
223 $TypeName =
"mult".$TypeName;
226 # encode the field for this row in a form value 227 print(
'<td>'.$Field->Name()
228 .
'<input type="hidden"' 229 .
' name="'.$this->EditFormName.
'[]"' 230 .
' class="field-subject field-static field-type-'.$TypeName.
'"' 231 .
' value="S_'.$Field->Id().
'"></td>'.
"\n");
233 print(
'<td><select name="'.$this->EditFormName.
'[]">'.
"\n");
236 $this->PrintOp(self::OP_NOP, $CurOp);
238 # determine operators, make a select widget 239 switch ($Field->Type())
244 $this->PrintOp(self::OP_APPEND, $CurOp);
245 $this->PrintOp(self::OP_PREPEND, $CurOp);
246 $this->PrintOp(self::OP_REPLACE, $CurOp);
247 $this->PrintOp(self::OP_FIND_REPLACE, $CurOp);
251 $this->PrintOp(self::OP_SET, $CurOp);
257 $this->PrintOp(self::OP_SET, $CurOp);
258 if ($Field->Optional())
260 $this->PrintOp(self::OP_CLEARALL, $CurOp);
268 $this->PrintOp(self::OP_SET, $CurOp);
269 $this->PrintOp(self::OP_CLEAR, $CurOp);
271 if ($Field->Optional() &&
273 $Field->AllowMultiple() ))
275 $this->PrintOp(self::OP_CLEARALL, $CurOp);
280 throw new Exception(
"Unsupported field type");
282 print(
'</select></td>');
285 switch ($Field->Type())
293 if ($CurOp == self::OP_FIND_REPLACE)
295 print(
'<input type="text" ' 296 .
'name="'.$this->EditFormName.
'[]" ' 297 .
'value="'.defaulthtmlentities($CurVal[0]).
'">');
298 print(
'<input type="text" class="field-value-repl" ' 299 .
'name="'.$this->EditFormName.
'[]" ' 300 .
'value="'.defaulthtmlentities($CurVal[1]).
'">');
304 print(
'<input type="text" ' 305 .
'name="'.$this->EditFormName.
'[]" ' 306 .
'value="'.defaulthtmlentities($CurVal).
'">');
313 print(
'<div class="field-value-qs cw-quicksearch ' 314 .
'cw-quicksearch-fieldid-'.$Field->Id().
'" ' 315 .
'data-fieldid="'.$Field->Id().
'">' 316 .
'<input class="cw-quicksearch-display ' 317 .
'cw-resourceeditor-metadatafield F_' 318 .$this->EditFormName.
'"' 319 .
'value="'.defaulthtmlentities($CurVal).
'" />' 320 .
'<input name="'.$this->EditFormName.
'[]"' 321 .
'class="cw-quicksearch-value" type="hidden"' 322 .
'value="'.defaulthtmlentitites($CurVal).
'" />' 323 .
'<div style="display: none;"' 324 .
'class="cw-quicksearch-menu">' 325 .
'<div class="cw-quicksearch-message ui-front"></div>' 331 print(
'<select name="'.$this->EditFormName.
'[]">');
332 foreach ($Field->GetPossibleValues() as $Id => $Val)
334 print(
'<option value="'.$Id.
'" ' 335 .
'class="field-id-'.$Field->Id().
'"' 336 .( ($CurVal == $Id) ?
' selected' :
'')
337 .
'>'.defaulthtmlentities($Val)
340 print(
'</select>'.
"\n");
347 # for selectable fields, we need to generate all the 348 # html elements that we might need and then depend on 349 # javascript to display only those that are relevant 351 # each field will have five elements 353 # 1. a field selector 354 print(
'<td><select name="'.$this->EditFormName.
'[]" ' 355 .
'class="field-subject">');
356 foreach ($FieldRow[
"SelectOptions"] as $FieldId)
359 $TypeName = defaulthtmlentities(
360 str_replace(
' ',
'', strtolower($Field->TypeAsName())));
363 $Field->AllowMultiple() )
365 $TypeName =
"mult".$TypeName;
368 if (!$Field->Optional())
370 $TypeName .=
" required";
373 print(
'<option class="field-type-'.$TypeName.
'" ' 374 .
'data-maxnumsearchresults="'.$Field->NumAjaxResults().
'" ' 375 .
'value="'.$Field->Id().
'">' 376 .defaulthtmlentities($Field->Name()).
'</option>');
378 print(
'</select></td>');
380 # 2. an operator selector 381 $TextTypes = array(
"url",
"text",
"paragraph");
383 print(
'<td><select name="'.$this->EditFormName.
'[]" ' 384 .
'class="field-operator">');
386 # for fields that cannot be removed, allow a 'do nothing' option 389 $this->PrintOp(self::OP_NOP, $CurOp,
390 array(
"flag",
"tree",
"option",
391 "multoption",
"controlledname",
392 "reference",
"timstamp",
"date",
396 # display all avaialble operators, annotated such that 397 # js can switch between them 398 $this->PrintOp(self::OP_SET, $CurOp,
399 array(
"flag",
"tree",
"option",
"multoption",
402 "timestamp",
"date",
"number"));
403 $this->PrintOp(self::OP_CLEAR, $CurOp,
404 array(
"tree",
"option",
405 "multoption",
"controlledname",
407 $this->PrintOp(self::OP_CLEARALL, $CurOp,
408 array(
"tree",
"controlledname",
"multoption",
409 "timestamp",
"date",
"number"));
411 $this->PrintOp(self::OP_APPEND, $CurOp, $TextTypes );
412 $this->PrintOp(self::OP_PREPEND, $CurOp, $TextTypes );
413 $this->PrintOp(self::OP_REPLACE, $CurOp, $TextTypes );
414 $this->PrintOp(self::OP_FIND_REPLACE, $CurOp, $TextTypes );
415 print(
'</select></td><td>');
417 # 3. a value selector (for option and flag values) 418 print(
'<select name="'.$this->EditFormName.
'[]" ' 419 .
'class="field-value-select">');
420 foreach ($FieldsExamined as $FieldId)
426 foreach ($Field->GetPossibleValues() as $Id => $Val)
428 print(
'<option value="'.$Id.
'" ' 429 .
'class="field-id-'.$Field->Id().
'">' 430 .defaulthtmlentities($Val)
437 # 4. two text entries (free-form text and possible replacement) 438 print(
'<input type="text" class="field-value-edit" ' 439 .
'name="'.$this->EditFormName.
'[]" ' 440 .
'value="'.defaulthtmlentities($CurVal).
'">' 441 .
'<input type="text" class="field-value-repl" ' 442 .
'name="'.$this->EditFormName.
'[]" ' 443 .
'value="'.defaulthtmlentities($CurVal).
'">');
445 # 5. an ajax search box 446 # CW-QuickSearch.js wants a textarea 447 print(
'<div class="field-value-qs cw-quicksearch ' 448 .
'cw-quicksearch-template">' 449 .
'<input class="cw-quicksearch-display ' 450 .
'cw-resourceeditor-metadatafield F_'.$this->EditFormName.
'" ' 451 .
'value="'.defaulthtmlentities($CurVal).
'" />' 452 .
'<input name="'.$this->EditFormName.
'[]" ' 453 .
'class="cw-quicksearch-value" type="hidden" ' 454 .
'value="'.defaulthtmlentities($CurVal).
'" />' 455 .
'<div style="display: none;" ' 456 .
'class="cw-quicksearch-menu">' 457 .
'<div class="cw-quicksearch-message ui-front"></div>' 460 if ($FieldRow[
"Type"] ==
"AddButton")
462 print(
'</tr><tr class="button_row"><td colspan="4">' 463 .
'<span class="cw-button cw-button-elegant cw-feui-add">' 464 .defaulthtmlentities($FieldRow[
"Label"]).
'</span></td></tr>');
481 if (!isset($_POST[$this->EditFormName]))
486 # extract the array of data associated with our EditFormName 487 $FormData = $_POST[$this->EditFormName];
489 while (count($FormData))
491 # first element of each row is a field id 492 $FieldId = array_shift($FormData);
493 $Op = array_shift($FormData);
495 # when the row was static, it'll have a 'S_' prefix 496 # to make it non-numeric 497 if (!is_numeric($FieldId))
499 # remove the S_ prefix to get the real field id 500 $FieldId = substr($FieldId, 2);
502 # grab the value(s) for this field 503 $Val = array_shift($FormData);
504 if ($Op == self::OP_FIND_REPLACE)
506 $Val2 = array_shift($FormData);
511 # for selectable fields, we'll have all possible 512 # elements and will need to grab the correct ones for 513 # the currently selected field 514 $SelectVal = array_shift($FormData);
515 $TextVal = array_shift($FormData);
516 $TextVal2 = array_shift($FormData);
517 $SearchVal = array_shift($FormData);
521 switch ($Field->Type())
544 throw new Exception(
"Unsupported field type");
549 "FieldId" => $FieldId,
"Op" => $Op,
"Val" => $Val );
551 if ($Op == self::OP_FIND_REPLACE)
553 $ResRow[
"Val2"] = $TextVal2;
569 foreach ($Data as $Row)
573 ($Row[
"Op"] == self::OP_FIND_REPLACE) ?
574 array( $Row[
"Val"], $Row[
"Val2"] ) :
594 foreach ($ChangesToApply as $Change)
598 if ( ($User === NULL ||
599 $Resource->UserCanEditField($User, $Field) ) &&
602 $OldVal = $Resource->Get( $Field );
604 switch ($Change[
"Op"])
610 self::ModifyFieldValue($User, $Resource, $Field, $Change[
"Val"]);
613 case self::OP_REPLACE:
614 if ( strlen($Change[
"Val"]) > 0 ||
617 self::ModifyFieldValue($User, $Resource,
618 $Field, $Change[
"Val"]);
622 case self::OP_FIND_REPLACE:
623 self::ModifyFieldValue(
627 str_replace($Change[
"Val"], $Change[
"Val2"], $OldVal));
631 if (isset($OldVal[$Change[
"Val"]]) &&
632 ($Field->Optional() || count($OldVal)>1) )
634 $Resource->Clear($Field, $Change[
"Val"]);
638 case self::OP_CLEARALL:
639 if ($Field->Optional())
641 $Resource->Clear($Field);
645 case self::OP_APPEND:
648 self::ModifyFieldValue($User, $Resource, $Field,
649 $OldVal.$Sep.$Change[
"Val"]);
652 case self::OP_PREPEND:
655 self::ModifyFieldValue($User, $Resource, $Field,
656 $Change[
"Val"].$Sep.$OldVal);
660 $NewVal = $Resource->Get( $Field );
661 if ($NewVal != $OldVal)
668 # if there were any changes 671 $Resource->UpdateAutoupdateFields(
675 # update search and recommender DBs if configured to do so 676 $Resource->QueueSearchAndRecommenderUpdate();
678 # signal the modified event if the resource isn't a temp one 679 if (!$Resource->IsTempResource())
681 $GLOBALS[
"AF"]->SignalEvent(
682 "EVENT_RESOURCE_MODIFY", array(
"Resource" => $Resource));
689 # ---- PRIVATE INTERFACE ------------------------------------------------- 691 private $EditFormName;
695 # mapping of operator constants to their friendly names 696 private $OpNames = array(
697 self::OP_NOP =>
"Do nothing",
698 self::OP_SET =>
"Set",
699 self::OP_CLEAR =>
"Clear One Value",
700 self::OP_CLEARALL =>
"Clear All Values",
701 self::OP_APPEND =>
"Append",
702 self::OP_PREPEND =>
"Prepend",
703 self::OP_REPLACE =>
"Replace With",
704 self::OP_FIND_REPLACE =>
"Find/Replace" );
707 private $AllowedFieldTypes;
717 private function PrintOp($Value, $Selected=NULL, $TypeNames=array())
720 foreach ($TypeNames as $Name)
722 $Classes[]=
"field-type-".$Name;
725 print(
'<option value="'.$Value.
'" ' 726 .($Selected==$Value ?
'selected' :
'')
727 .
'class="'.implode(
' ', $Classes).
'"' 728 .
'>'.$this->OpNames[$Value]
738 private function TypesOrIdsToFieldList($FieldTypesOrIds)
740 if ($FieldTypesOrIds === NULL)
742 $FieldTypesOrIds = $this->AllowedFieldTypes;
745 $FieldList = array();
747 if (is_array($FieldTypesOrIds))
749 # if we were given a list of fields, add all the editable ones 750 foreach ($FieldTypesOrIds as $FieldId)
752 if ($this->Schema->FieldExists($FieldId))
754 $Field = $this->Schema->GetField($FieldId);
755 if ($Field->Editable())
757 $FieldList[]= $FieldId;
764 # otherwise, iterate over all supported fields and add the editable ones 765 foreach ($this->Schema->GetFields($FieldTypesOrIds) as $Field)
767 if ($Field->Editable())
769 $FieldList[]= $Field->Id();
785 private static function ModifyFieldValue($User, $Resource, $Field, $NewValue)
787 $ShouldDoDateSubs = array(
794 $ShouldDoUserSubs = array(
800 # process substitutions for fields where they apply 801 if (in_array($Field->Type(), $ShouldDoDateSubs))
803 $Substitutions = array(
804 "X-DATE-X" => date(
"M j Y"),
805 "X-TIME-X" => date(
"g:ia T") );
807 $NewValue = str_replace(
808 array_keys($Substitutions),
809 array_values($Substitutions),
813 if (in_array($Field->Type(), $ShouldDoUserSubs))
815 $Substitutions = array(
816 "X-USERNAME-X" => ($User!==NULL) ? $User->Get(
"UserName") :
"",
817 "X-USEREMAIL-X" => ($User!==NULL) ? $User->Get(
"EMail") :
"",
820 $NewValue = str_replace(
821 array_keys($Substitutions),
822 array_values($Substitutions),
826 # process edit hooks for fields where they apply 827 $ShouldCallHooks = array(
836 if (in_array($Field->Type(), $ShouldCallHooks))
838 $SignalResult = $GLOBALS[
"AF"]->SignalEvent(
839 "EVENT_POST_FIELD_EDIT_FILTER", array(
841 "Resource" => $Resource,
842 "Value" => $NewValue));
843 $NewValue = $SignalResult[
"Value"];
846 # before updating field value, verify that the provided item is real 847 $Factory = $Field->GetFactory();
848 if (is_null($Factory) ||
849 $Factory->ItemExists($NewValue) !== FALSE)
851 $Resource->Set($Field, $NewValue);
LoadConfiguration($Data)
Load a configured set of fields.
DisplayAsRows()
Display the table rows for the editing form, without the surrounding.
AddField($FieldNameOrId, $CurrentValue=NULL, $CurrentOperator=NULL, $AllowRemoval=FALSE)
Add a field to the list of editable fields.
AddFieldButton($Label="Add field", $FieldTypesOrIds=NULL)
Add a button to create more fields above the button.
DisplayAsTable($TableId=NULL, $TableStyle=NULL)
Display editing form elements enclosed in a.
static ApplyChangesToResource($Resource, $User, $ChangesToApply)
Apply the changes extracted from an editing form to a specified resource.
__construct($EditFormName, $SchemaId=MetadataSchema::SCHEMAID_DEFAULT)
Create a UI for specifing edits to metadata fields.
GetValuesFromFormData()
Extract values from a dynamics field edit/modification form.
AddSelectableField($FieldTypesOrIds=NULL, $CurrentFieldId=NULL, $CurrentValue=NULL, $CurrentOperator=NULL, $AllowRemoval=TRUE)
Add a selectable field to the list of editable fields.