diff --git a/src/EditableManyField.php b/src/EditableManyField.php new file mode 100644 index 0000000..7c8ecf3 --- /dev/null +++ b/src/EditableManyField.php @@ -0,0 +1,235 @@ + EditableFormField::class + ]; + + private static $owns = [ + 'Children' + ]; + + private static $cascade_deletes = [ + 'Children' + ]; + + private static $cascade_duplicates = [ + 'Children' + ]; + + /** + * @return FieldList + */ + public function getCMSFields() + { + $this->beforeUpdateCMSFields(function (FieldList $fields) { + $editableColumns = new GridFieldEditableColumns(); + $fieldClasses = singleton(EditableFormField::class)->getEditableFieldClasses(); + $editableColumns->setDisplayFields([ + 'ClassName' => function ($record, $column, $grid) use ($fieldClasses) { + if ($record instanceof EditableFormField) { + $field = $record->getInlineClassnameField($column, $fieldClasses); + if ($record instanceof EditableFileField) { + $field->setAttribute('data-folderconfirmed', $record->FolderConfirmed ? 1 : 0); + } + return $field; + } + }, + 'Title' => function ($record, $column, $grid) { + if ($record instanceof EditableFormField) { + return $record->getInlineTitleField($column); + } + } + ]); + + $config = GridFieldConfig::create() + ->addComponents( + $editableColumns, + new GridFieldButtonRow(), + (new GridFieldAddClassesButton(EditableTextField::class)) + ->setButtonName(_t(__CLASS__ . '.ADD_FIELD', 'Add Field')) + ->setButtonClass('btn-primary'), + (new GridFieldAddClassesButton(EditableFormStep::class)) + ->setButtonName(_t(__CLASS__ . '.ADD_PAGE_BREAK', 'Add Page Break')) + ->setButtonClass('btn-secondary'), + (new GridFieldAddClassesButton([EditableFieldGroup::class, EditableFieldGroupEnd::class])) + ->setButtonName(_t(__CLASS__ . '.ADD_FIELD_GROUP', 'Add Field Group')) + ->setButtonClass('btn-secondary'), + $editButton = new GridFieldEditButton(), + new GridFieldDeleteAction(), + new GridFieldToolbarHeader(), + new GridFieldOrderableRows('Sort'), + new GridFieldDetailForm(), + // Betterbuttons prev and next is enabled by adding a GridFieldPaginator component + new GridFieldPaginator(999) + ); + + $fields->addFieldsToTab('Root.Main', [ + GridField::create( + 'Children', + 'Children', + $this->Children(), + $config + ) + ]); + }); + + return parent::getCMSFields(); + } + + /** + * Return the form field + * + */ + public function getFormField() + { + $children = []; + foreach ($this->Children() as $editableFormField) { + $children[] = $editableFormField->getFormField(); + } + + $field = ManyField::create($this->Name, FieldList::create($children)) + ->setCanSort(false) + ->setMinRecords(1); + + $this->doUpdateFormField($field); + + return $field; + } + + + public function getSubmittedFormField(): SubmittedManyFormField + { + return SubmittedManyFormField::create(); + } + + + /** + * When saving this data from the front end, extract the array and + * create the children records + */ + public function getValueFromData($data) + { + $incoming = isset($data[$this->Name]) ? $data[$this->Name] : false; + + if (!$incoming) { + return json_encode([]); + } + + // unset any rows which don't have any values at all + $rowHasValue = []; + + foreach ($this->Children() as $field) + { + foreach ($incoming[$field->Name] as $i => $value) { + if ($value && !empty($value)) { + $rowHasValue[$i] = true; + } + } + } + + $rows = []; + + foreach ($this->Children() as $field) + { + foreach ($incoming[$field->Name] as $i => $value) { + if (!isset($rowHasValue[$i])) { + // empty row; + continue; + } + + if (!isset($rows[$i])) { + $rows[$i] = []; + } + + $submittedField = $this->createNestedSubmittedFormField($field, [ + $field->Name => $value + ]); + + $rows[$i][$field->Name] = $submittedField->ID; + } + } + + return json_encode($rows); + } + + + public function createNestedSubmittedFormField(EditableFormField $field, $data) + { + $submittedField = $field->getSubmittedFormField(); + $submittedField->Name = $field->Name; + $submittedField->Title = $field->getField('Title'); + + // save the value from the data + if ($field->hasMethod('getValueFromData')) { + $submittedField->Value = $field->getValueFromData($data); + } else { + if (isset($data[$field->Name])) { + $submittedField->Value = $data[$field->Name]; + } + } + + if (!empty($data[$field->Name])) { + if (in_array(EditableFileField::class, $field->getClassAncestry())) { + if (!empty($_FILES[$field->Name]['name'])) { + $foldername = $field->getFormField()->getFolderName(); + $upload = Upload::create(); + + try { + $upload->loadIntoFile($_FILES[$field->Name], null, $foldername); + + /** @var AssetContainer|File $file */ + $file = $upload->getFile(); + $file->ShowInSearch = 0; + $file->UserFormUpload = UserFormFileExtension::USER_FORM_UPLOAD_TRUE; + $file->write(); + + $submittedField->UploadedFileID = $file->ID; + } catch (ValidationException $e) { + + } + } + } + } + + $submittedField->extend('onPopulationFromField', $field); + $submittedField->write(); + + return $submittedField; + } +} diff --git a/src/ManyField.php b/src/ManyField.php index 0df543e..6089dd4 100644 --- a/src/ManyField.php +++ b/src/ManyField.php @@ -610,7 +610,7 @@ protected function updateManyNestedField($field, $index, $value, $prefixName) { } } } else { - if ($value && $value->hasMethod($name)) { + if ($value && is_object($value) && $value->hasMethod($name)) { $field->setValue($value->{$name}(), $value); } else if (is_object($value)) { $field->setValue($value->{$name}, $value); @@ -660,7 +660,7 @@ public function generateRow($index, $value = null, $prefixName = true) $field = $field->setReadonly($this->readonly); $field = $field->setDisabled($this->readonly); - if ($value && $value->hasMethod('modifyManyRecordField')) { + if ($value && is_object($value) && $value->hasMethod('modifyManyRecordField')) { $field = $value->modifyManyRecordField($field); } diff --git a/src/SubmittedManyFormField.php b/src/SubmittedManyFormField.php new file mode 100644 index 0000000..c71e440 --- /dev/null +++ b/src/SubmittedManyFormField.php @@ -0,0 +1,152 @@ + 'Boolean' + ]; + + private static $many_many = [ + 'Children' => SubmittedFormField::class + ]; + + private static $many_many_extraFields = [ + 'Children' => [ + 'Row' => 'Int' + ] + ]; + + private static $cascade_deletes = [ + 'Children' + ]; + + + public function getCMSFields() + { + $fields = parent::getCMSFields(); + $fields->removeByName('Processed'); + + return $fields; + } + + + public function onAfterWrite() + { + parent::onAfterWrite(); + + if (!$this->Processed) { + if ($rows = json_decode($this->Value, true)) { + foreach ($rows as $i => $ids) { + foreach ($ids as $fieldName => $id) { + $this->Children()->add($id, [ + 'Row' => $i + ]); + } + } + } + + DB::query('UPDATE SubmittedManyFormField SET Processed = 1 WHERE ID = '. $this->ID); + } + } + + + public function getFormattedValue() + { + return $this->renderWith('Includes/SubmittedManyFormField'); + } + + + /** + * Return the value of this submitted form field suitable for inclusion + * into the CSV + * + * @return DBField + */ + public function getExportValue() + { + return $this->Value; + } + + + /** + * Returns the rows of the many field. Optionally includes a header row + * if it is needed. As the order of the fields may change submission to + * submission, it first sorts by the latest row (the newest configuration) + * + */ + public function getRows() + { + $rows = []; + $i = 0; + $max = null; + $columns = []; + + foreach ($this->Children() as $child) { + if (!isset($rows[$child->Row])) { + $rows[$child->Row] = []; + } + + $rows[$child->Row][$child->Name] = $child; + + if (!$max || $max < $child->Row) { + $max = $child->Row; + } + + $columns[$child->Name] = $child->Title; + } + + $output = ArrayList::create(); + $lastHeader = null; + + foreach ($rows as $row => $data) { + if (!$lastHeader) { + $header = ArrayList::create(); + + foreach ($columns as $name => $title) { + $header->push(ArrayData::create([ + 'Title' => $title + ])); + } + + $lastHeader = $header; + } else { + $header = null; + } + + $columnData = ArrayList::create(); + + foreach ($columns as $name => $column) { + $value = SubmittedFormField::create(); + + foreach ($data as $field) { + if ($field->Name === $name) { + $value = $field; + + break; + } + } + + $columnData->push($value); + } + + $output->push(ArrayData::create([ + 'HeaderRow' => $header, + 'Columns' => $columnData + ])); + } + + return $output; + } +} diff --git a/templates/Includes/SubmittedManyFormField.ss b/templates/Includes/SubmittedManyFormField.ss new file mode 100644 index 0000000..e811cb9 --- /dev/null +++ b/templates/Includes/SubmittedManyFormField.ss @@ -0,0 +1,15 @@ +
$Title | <% end_loop %> +
---|
{$FormattedValue} | + <% end_loop %> +