Source of file AnswerInputField.php
Size: 16,284 Bytes - Last Modified: 2021-12-23T10:08:54+00:00
/var/www/docs.ssmods.com/process/src/src/Model/AnswerInputField.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 | <?php /** * This file contains the "AnswerInputField" class. * * @category SilverStripe_Project * @package SDLT * @author Catalyst I.T. SilverStripe Team 2018 <silverstripedev@catalyst.net.nz> * @copyright NZ Transport Agency * @license BSD-3 * @link https://www.catalyst.net.nz */ namespace NZTA\SDLT\Model; use SilverStripe\GraphQL\Scaffolding\Interfaces\ScaffoldingProvider; use SilverStripe\GraphQL\Scaffolding\Scaffolders\SchemaScaffolder; use SilverStripe\ORM\DataObject; use SilverStripe\Security\Member; use SilverStripe\Security\Security; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\ListboxField; use SilverStripe\Forms\NumericField; use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter; use UncleCheese\DisplayLogic\Forms\Wrapper; use NZTA\SDLT\Traits\SDLTModelPermissions; use NZTA\SDLT\Model\MultiChoiceAnswerSelection; use SilverStripe\Core\Convert; use SilverStripe\Versioned\Versioned; use SilverStripe\SnapshotAdmin\SnapshotHistoryExtension; /** * Class AnswerInputField * * @property string Name * @property string Type * @property Question Question */ class AnswerInputField extends DataObject implements ScaffoldingProvider { use SDLTModelPermissions; /** * @var int */ const MAX_URL_LENGTH = 4096; /** * @var string */ private static $table_name = 'AnswerInputField'; /** * @var array */ private static $db = [ 'Label' => 'Varchar(255)', 'InputType' => 'Enum("text, email, textarea, product aspects, date, release date, url, multiple-choice: single selection, multiple-choice: multiple selection", "text")', 'Required' => 'Boolean', 'MinLength' => 'Int', 'MaxLength' => 'Int', 'PlaceHolder' => 'Varchar(255)', 'SortOrder' => 'Int', 'IsBusinessOwner' => 'Boolean', 'IsProductName' => 'Boolean', 'MultiChoiceSingleAnswerDefault' => 'Varchar(255)', 'MultiChoiceMultipleAnswerDefault' => 'Varchar(255)', ]; /** * @var string */ private static $extensions = [ Versioned::class . '.versioned', SnapshotHistoryExtension::class ]; /** * @var string */ private static $default_sort = 'SortOrder'; /** * @var array */ private static $has_one = [ 'Question' => Question::class ]; /** * @var array */ private static $has_many = [ 'AnswerSelections' => MultiChoiceAnswerSelection::class, ]; /** * @var array */ private static $defaults = [ 'MaxLength' => self::MAX_URL_LENGTH, ]; /** * @var array */ private static $summary_fields = [ 'Label', 'InputType' ]; /** * @var array */ private static $field_labels = [ 'Label' => 'Field Label', 'InputType' => 'Field Type' ]; /** * @return FieldList */ public function getCMSFields() { $fields = parent::getCMSFields(); $fields->removeByName([ 'QuestionID', 'SortOrder', 'MultiChoiceSingleAnswerDefault', 'MultiChoiceMultipleAnswerDefault', 'MinLength', 'MaxLength', ]); $multiChoiceAnswerValues = $this->AnswerSelections() ->map('Value', 'Label') ->toArray(); $blocksField = $fields->dataFieldByName('AnswerSelections'); $fields->removeByName('AnswerSelections'); // <-- Removes the scaffolded tab if ($this->exists()) { $config = $blocksField->getConfig(); $config->removeComponent($config->getComponentByType(GridFieldAddExistingAutocompleter::class)); $fields->addFieldToTab( 'Root.Main', Wrapper::create($blocksField) ->hideUnless('InputType') ->startsWith('multiple-choice') ->end() ); } // Multi-choice default selection fields $fields->insertAfter( 'InputType', Wrapper::create( FieldList::create([ DropdownField::create( 'MultiChoiceSingleAnswerDefault', 'Radio Button Default Selection', $multiChoiceAnswerValues ?: [] ) ->setEmptyString('(none)') ->setDescription( '' . 'This selection represents which of the related ' . 'question\'s radio buttons, is selected by default. Once values have ' . 'been added, a default can be chosen.' ) ->setDisabled(!$multiChoiceAnswerValues) ->setAttribute('style', 'width: 200px;') ->hideIf('InputType') ->startsWith('multiple-choice: multiple') ->end(), Wrapper::create(ListboxField::create( 'MultiChoiceMultipleAnswerDefault', 'Checkbox Default Selections', $multiChoiceAnswerValues ?: [] ) ->setDisabled(!$multiChoiceAnswerValues) ->setDescription( '' . 'These selections represent which of the related ' . 'question\'s checkboxes are checked by default. ' . 'Once values have been added below, defaults can be chosen.' )) ->hideUnless('InputType') ->startsWith('multiple-choice: multiple') ->end() ]) ) ->displayIf('InputType') ->startsWith('multiple-choice:') ->end() ); $fields->addFieldsToTab('Root.Main', FieldList::create([ NumericField::create('MinLength', 'Min Length'), NumericField::create('MaxLength', 'Max Length') ->setHTML5(true) // <-- Removes silly i18n thousands separator ->setDisabled(true), ]), 'PlaceHolder'); /** @noinspection PhpUndefinedMethodInspection */ $fields->dataFieldByName('IsBusinessOwner') ->displayIf('InputType') ->isEqualTo('email'); $fields->dataFieldByName('IsProductName') ->setTitle('Does this field contain a product name?') ->displayIf('InputType') ->isEqualTo('text'); $fields->dataFieldByName('MinLength') ->displayUnless('InputType') ->startsWith('multiple-choice'); $fields->dataFieldByName('MaxLength') ->displayUnless('InputType') ->startsWith('multiple-choice'); $fields->dataFieldByName('PlaceHolder') ->displayUnless('InputType') ->startsWith('multiple-choice'); return $fields; } /** * @param SchemaScaffolder $scaffolder Scaffolder * @return SchemaScaffolder */ public function provideGraphQLScaffolding(SchemaScaffolder $scaffolder) { // Provide entity type $scaffolder ->type(AnswerInputField::class) ->addFields([ 'ID', 'Label', 'InputType', 'Required', 'MinLength', 'MaxLength', 'GQLMultiChoiceAnswer' => 'Contains json-encoded, serialized data, representing multiple-choice answers.', 'MultiChoiceSingleAnswerDefault' => 'An integer representing the default, single-selection, multiple-choice option.', 'MultiChoiceMultipleAnswerDefault' => 'Contains json-encoded, serialized data, representing default multi-selections.', ]); return $scaffolder; } /** * OverLoaded getter for the "MultiChoiceAnswer" field. See the following issue * on GH for context for why this is needed: https://github.com/silverstripe/silverstripe-graphql/issues/234. * * @return string A JSON-encoded string of field label/values. */ public function getGQLMultiChoiceAnswer() { $selections = $this->AnswerSelections(); $optionData = []; if ($selections->exists()) { foreach ($selections as $selection) { $data['value'] = Convert::html2raw($selection->Value); $data['label'] = $selection->Label; //ensure the Risks key always exists as an array $risks = $selection->Risks()->toNestedArray(); if ($risks) { //avoid un-needed fields from JSON response //reduces network payload and avoids info disclosure foreach ($risks as $idx => $risk) { unset($risk['ClassName'], $risk['LastEdited'], $risk['Created'], $risk['RecordClassName']); $risks[$idx] = $risk; } } $data['Risks'] = $risks ?: []; $optionData[] = $data; } } return json_encode($optionData); } /** * @return boolean */ public function isMultipleChoice() : bool { return strstr($this->InputType, 'multiple-choice'); } /** * @return boolean */ public function isMultipleChoiceSingle() : bool { if (!$this->isMultipleChoice()) { return false; } return $this->InputType === 'multiple-choice: single selection'; } /** * get current object link in model admin * @return string */ public function getLink() { $questionLink = $this->Question->getLink(""); if ($questionLink) { $link = $questionLink . "/ItemEditForm/field/AnswerActionFields/item/" . $this->ID; return $link; } else { return null; } } /** * Deal with pre-write processes. * * @return void */ public function onBeforeWrite() { parent::onBeforeWrite(); $this->auditService->audit($this); } /** * Allow logged-in user to access the model * * @param Member|null $member member * @return bool */ public function canView($member = null) { return (Security::getCurrentUser() !== null); } /** * Return all the {@link Risk} objects related to an answer. * * @return array */ public function getRisks() : array { $risks = []; foreach ($this->AnswerSelections() as $selection) { foreach ($selection->Risks() as $risk) { $risks[] = $risk; } } return $risks; } /** * question has many input field and input field type can be MultiChoiceAnswer. * If question has input field type MultiChoiceAnswer (radio/checkbox) * then get all the risks of the selected options * * @param array $inputFields question has many input field * @param array $answers answer array of the input fields * * @return array */ public static function get_risk_for_input_fields($inputFields, $answers) : array { $selectedOptionRisks = []; // traverse question's input fields foreach ($inputFields as $inputField) { // if input type isn't MultiChoiceAnswer (radio/checkbox) // then continue for next input field if (!isset($inputField['MultiChoiceAnswer']) || !$options = json_decode($inputField['MultiChoiceAnswer'], true) ) { continue; } // if input type is MultiChoiceAnswer then collect input field id $inputFieldID = $inputField['ID']; $inputFieldAnswer = []; // get the answer for the input field // filter if input field id exists in $answers['inputs'] array $inputFieldAnswer = array_filter($answers['inputs'], function ($e) use ($inputFieldID) { return isset($e['id']) && $e['id'] == $inputFieldID; }); // if there is no answer for the input field, // then continue for next input field if (empty($inputFieldAnswer)) { continue; } // get answer array from $inputFieldAnswer $answer = array_pop($inputFieldAnswer); $selectedOption = $answer['data']; // string for radio if ($inputField['InputType'] === 'multiple-choice: multiple selection') { $selectedOption = json_decode($selectedOption); // array for checkbox } // traverse all the option of multi-choice input field type foreach ($options as $option) { if (!in_array($option['value'], (array)$selectedOption)) { continue; } // merge all the risks for the selected options $selectedOptionRisks = isset($option['Risks']) ? array_merge( $selectedOptionRisks, array_values($option['Risks']) ): []; } } return $selectedOptionRisks; } /** * create input field from json import * * @param object $inputFieldJson input field json object * @return DataObject */ public static function create_record_from_json($inputFieldJson) { $obj = self::create(); $obj->Label = $inputFieldJson->label; $obj->InputType = $inputFieldJson->inputType ?? 'text'; $obj->Required = $inputFieldJson->required ?? false; $obj->MinLength = $inputFieldJson->minLength ?? 0; $obj->MaxLength = $inputFieldJson->maxLength ?? 4096; $obj->PlaceHolder = $inputFieldJson->placeHolder ?? ''; $obj->IsBusinessOwner = $inputFieldJson->isBusinessOwner ?? false; $obj->IsProductName = $inputFieldJson->isProductName ?? false; $obj->MultiChoiceSingleAnswerDefault = $inputFieldJson->multiChoiceSingleAnswerDefault ?? ''; $obj->MultiChoiceMultipleAnswerDefault = $inputFieldJson->multiChoiceMultipleAnswerDefault ?? ''; // if field type is multi select (radio or checkobox) then add option field if (property_exists($inputFieldJson, "answerSelections") && !empty($selections = $inputFieldJson->answerSelections)) { foreach ($selections as $selection) { $dbSelection = MultiChoiceAnswerSelection::create_record_from_json($selection); $obj->AnswerSelections()->add($dbSelection); } } $obj->write(); return $obj; } /** * export inputField * * @param object $inputField inputField * @return array */ public static function export_record($inputField) { $obj['label'] = $inputField->Label ?? ''; $obj['inputType'] = $inputField->InputType; $obj['required'] = (boolean) $inputField->Required; $obj['minLength'] = $inputField->MinLength; $obj['maxLength'] = $inputField->MaxLength; $obj['placeHolder'] = $inputField->PlaceHolder?? ''; $obj['isBusinessOwner'] = (boolean) $inputField->IsBusinessOwner; $obj['isProductName'] = (boolean) $inputField->IsProductName; if ($inputField->isMultipleChoice()) { $obj['multiChoiceSingleAnswerDefault'] = $inputField->MultiChoiceSingleAnswerDefault ?? ''; $obj['multiChoiceMultipleAnswerDefault'] = $inputField->MultiChoiceMultipleAnswerDefault ?? ''; foreach ($inputField->AnswerSelections() as $selection) { $obj['answerSelections'][] = MultiChoiceAnswerSelection::export_record($selection); } } return $obj; } } |