diff --git a/css/ArrayListField.scss.css b/css/ArrayListField.scss.css index fab0f12..894b91c 100644 --- a/css/ArrayListField.scss.css +++ b/css/ArrayListField.scss.css @@ -40,12 +40,15 @@ width: 16px; cursor: move; background: center center no-repeat url("../images/orderable-handle.png"); } - .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .delete-record { + .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .orderable-up, .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .orderable-down, .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .delete-record { padding: 2px; } - .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .delete-record:before { + .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .orderable-up:before, .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .orderable-down:before, .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .delete-record:before { margin: 0; } - .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .delete-record span { + .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .orderable-up span, .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .orderable-down span, .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .delete-record span { display: none; } - .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .delete-record:hover { + .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .orderable-up:hover, .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .orderable-down:hover, .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record .controls .delete-record:hover { background: transparent; opacity: .6; } + .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record:first-child .controls .orderable-up, .zauberfisch\\SerializedDataObject\\Form\\ArrayListField div.record-list .record:last-child .controls .orderable-down { + cursor: default; + opacity: .4 !important; } diff --git a/javascript/ArrayListField.js b/javascript/ArrayListField.js index 9008121..55ac720 100644 --- a/javascript/ArrayListField.js +++ b/javascript/ArrayListField.js @@ -14,21 +14,25 @@ }); $('.zauberfisch\\\\SerializedDataObject\\\\Form\\\\ArrayListField.orderable .record-list').entwine({ onmatch: function () { + this.sortableEnable(); + this._super(); + }, + onunmatch: function () { + this.sortableDisable(); + this._super(); + }, + sortableEnable: function () { // enable sorting functionality - var self = this, - rootForm = this.closest('form'); - self.sortable({ + this.sortable({ handle: ".orderable-handle", axis: "y" }); - this._super(); }, - onunmatch: function () { + sortableDisable: function () { try { - $(this).sortable("destroy"); + this.sortable("destroy"); } catch (e) { } - this._super(); } }); $('.zauberfisch\\\\SerializedDataObject\\\\Form\\\\ArrayListField .add-record').entwine({ @@ -65,5 +69,29 @@ return false; } }); + $('.zauberfisch\\\\SerializedDataObject\\\\Form\\\\ArrayListField .orderable-up, .zauberfisch\\\\SerializedDataObject\\\\Form\\\\ArrayListField .orderable-down').entwine({ + onclick: function () { + var record = this.closest('.record'), + recordList = this.getContainerField().getRecordList(), + index = record.index(); + console.log(index); + console.log(recordList.find('.record').length - 1); + if ( + (index === 0 && this.hasClass('orderable-up')) || + (index === recordList.find('.record').length - 1 && this.hasClass('orderable-down')) + ) { + return false; + } + recordList.sortableDisable(); + if (this.hasClass('orderable-up')) { + record.prev().insertAfter(record); + } else { + record.next().insertBefore(record); + } + recordList.sortableEnable(); + this.blur(); + return false; + } + }); }) (jQuery); diff --git a/scss/ArrayListField.scss b/scss/ArrayListField.scss index ec91431..513cb8f 100644 --- a/scss/ArrayListField.scss +++ b/scss/ArrayListField.scss @@ -20,12 +20,12 @@ border: 1px solid #CDCCD0; background: rgba(#000, .05); margin: 0 0 10px; - + .record { padding: 20px 10px 10px; border-bottom: 1px solid #CDCCD0; position: relative; - + &:last-child { border-bottom: 0; } @@ -36,22 +36,22 @@ position: absolute; right: 5px; top: 10px; - + > * { display: block; float: left; margin: 0 5px 0 0; } - + .orderable-handle { height: 22px; width: 16px; cursor: move; background: center center no-repeat url('../images/orderable-handle.png'); } - .delete-record { + .orderable-up, .orderable-down, .delete-record { padding: 2px; - + &:before { margin: 0; } @@ -64,6 +64,10 @@ } } } + &:first-child .controls .orderable-up, &:last-child .controls .orderable-down { + cursor: default; + opacity: .4 !important; + } } } } diff --git a/src/DBField/AbstractField.php b/src/DBField/AbstractField.php index 03a1376..dd25790 100644 --- a/src/DBField/AbstractField.php +++ b/src/DBField/AbstractField.php @@ -27,7 +27,7 @@ function getValue() { } /** - * @param AbstractList|AbstractDataObject|AbstractField|array|null|string $value + * @param AbstractList|AbstractDataObject|AbstractField|null|string $value * @param null $record * @param bool|true $markAsChanged */ diff --git a/src/DBField/AbstractListField.php b/src/DBField/AbstractListField.php index c87c15e..b85862c 100644 --- a/src/DBField/AbstractListField.php +++ b/src/DBField/AbstractListField.php @@ -2,8 +2,6 @@ namespace zauberfisch\SerializedDataObject\DBField; -use zauberfisch\SerializedDataObject\ArrayList; - /** * @author Zauberfisch */ diff --git a/src/DBField/SortedDataListField.php b/src/DBField/SortedDataListField.php new file mode 100644 index 0000000..c8c5782 --- /dev/null +++ b/src/DBField/SortedDataListField.php @@ -0,0 +1,14 @@ +recordClassName = $recordClassName; parent::__construct($name, $title); } - + /** * @param ArrayListDBField|array|string $val * @return $this @@ -34,7 +34,7 @@ public function setValue($val) { // value is an array after form submission, lets turn it into an object if (is_array($val)) { $this->value->setValue($this->createValueFromArray($val)); - } elseif (is_string($val)) { + } else if (is_string($val)) { $this->value->setValue($val); } else { throw new \Exception('unexpected value'); @@ -43,9 +43,10 @@ public function setValue($val) { } return $this; } - + /** - * @return ArrayListDBField + * @return \zauberfisch\SerializedDataObject\DBField\ArrayListField + * @throws \Exception */ public function Value() { $return = parent::Value(); @@ -55,7 +56,12 @@ public function Value() { } return $return; } - + + /** + * @param $array + * @return ArrayList + * @throws \Exception + */ public function createValueFromArray($array) { $records = []; $class = $this->recordClassName; @@ -67,14 +73,14 @@ public function createValueFromArray($array) { } return new ArrayList($records); } - + /** * @return array */ public function getAttributes() { return $this->attributes; } - + /** * @param array $properties * @return string @@ -88,7 +94,7 @@ public function FieldHolder($properties = []) { $this->setAttribute('data-add-record-url', $this->getAddRecordLink()); return parent::FieldHolder($properties); } - + /** * @param array $properties * @return string @@ -108,13 +114,13 @@ public function Field($properties = []) { /** @noinspection PhpParamsInspection */ return (new \CompositeField([ (new \CompositeField($fields))->addExtraClass('record-list'), - (new \FormAction('addRecord', _t(self::class . '.AddRecord', 'add record'))) + (new \FormAction('addRecord', _t('ArrayListField.AddRecord', 'add record'))) ->setUseButtonTag(true) ->addExtraClass('font-icon-plus') ->addExtraClass('add-record'), ]))->FieldHolder()->forTemplate(); } - + /** * @param int $index * @param AbstractDataObject|null $record @@ -137,9 +143,17 @@ protected function getRecordFields($index, AbstractDataObject $record = null) { ->setUseButtonTag(true) ->addExtraClass('delete-record') ->addExtraClass('font-icon-cancel-circled') - ->setAttribute('data-confirm', _t(self::class . '.ConfirmDelete', 'Are you sure you want to delete this record?')), + ->setAttribute('data-confirm', _t('ArrayListField.ConfirmDelete', 'Are you sure you want to delete this record?')), ]; if ($this->orderable) { + $controls [] = (new \FormAction('ArrayListFieldControlsOrderableUp', '')) + ->setUseButtonTag(true) + ->addExtraClass('orderable-up') + ->addExtraClass('font-icon-up-open-big'); + $controls [] = (new \FormAction('ArrayListFieldControlsOrderableDown', '')) + ->setUseButtonTag(true) + ->addExtraClass('orderable-down') + ->addExtraClass('font-icon-down-open-big'); $controls [] = new \LiteralField('ArrayListFieldControlsOrderableHandle', '
'); } $recordFields->unshift( @@ -154,32 +168,32 @@ protected function getRecordFields($index, AbstractDataObject $record = null) { } return (new \CompositeField($recordFields))->addExtraClass('record'); } - + const MERGE_DEFAULT = 0; const MERGE_CLEAR_MISSING = 1; const MERGE_IGNORE_FALSEISH = 2; - + protected function loadDataFromRecord($dataFields, $data) { $mergeStrategy = 0; foreach ($dataFields as $field) { /** @var \FormField $field */ $name = $field->getName(); - + // First check looks for (fieldname)_unchanged, an indicator that we shouldn't overwrite the field value if (is_array($data) && isset($data[$name . '_unchanged'])) continue; - + // Does this property exist on $data? $exists = false; // The value from $data for this field $val = null; - + if (is_object($data)) { $exists = ( isset($data->$name) || $data->hasMethod($name) || ($data->hasMethod('hasField') && $data->hasField($name)) ); - + if ($exists) { $val = $data->__get($name); } @@ -191,11 +205,11 @@ protected function loadDataFromRecord($dataFields, $data) { else if (preg_match_all('/(.*)\[(.*)\]/U', $name, $matches)) { //discard first match which is just the whole string array_shift($matches); - + $keys = array_pop($matches); $name = array_shift($matches); $name = array_shift($name); - + if (array_key_exists($name, $data)) { $tmpData = &$data[$name]; // drill down into the data array looking for the corresponding value @@ -216,7 +230,7 @@ protected function loadDataFromRecord($dataFields, $data) { } } } - + // save to the field if either a value is given, or loading of blank/undefined values is forced if ($exists) { if ($val != false || ($mergeStrategy & self::MERGE_IGNORE_FALSEISH) != self::MERGE_IGNORE_FALSEISH) { @@ -228,7 +242,7 @@ protected function loadDataFromRecord($dataFields, $data) { } } } - + /** * @param \FieldList $fields */ @@ -244,11 +258,11 @@ protected function prefixRecordFields($index, $fields) { } } } - + public function getPrefixedRecordFieldName($index, $fieldName) { return sprintf('%s[%s][%s]', $this->getName(), $index, $fieldName); } - + public function handleSubField($fullFieldName) { $str = substr($fullFieldName, strlen($this->getName())); if (preg_match('/^\[(\d*)\]/', $str, $matches)) { @@ -262,27 +276,27 @@ public function handleSubField($fullFieldName) { } return null; } - + /** * @param \DataObjectInterface $record */ public function saveInto(\DataObjectInterface $record) { $record->{$this->name} = $this->Value(); } - + private static $allowed_actions = [ 'addRecord', ]; - + public function addRecord(\SS_HTTPRequest $r) { $index = (int)$r->getVar('index'); return $this->getRecordFields($index)->FieldHolder()->forTemplate(); } - + public function getAddRecordLink() { return $this->Link('addRecord'); } - + /** * @param bool $bool * @return ArrayListField @@ -291,18 +305,18 @@ public function setOrderable($bool) { $this->orderable = $bool; return $this; } - + /** * @return bool */ public function isOrderable() { return $this->orderable; } - + public function setForm($form) { parent::setForm($form); } - + /** * @param callable $recordFieldsCallback * @return ArrayListField @@ -311,7 +325,7 @@ public function setRecordFieldsCallback($recordFieldsCallback) { $this->recordFieldsCallback = $recordFieldsCallback; return $this; } - + /** * @return callable */ @@ -333,7 +347,7 @@ public function getRecordFieldsCallback() { } return $callback; } - + /** * @param callable $recordFieldsUpdateCallback * @return ArrayListField @@ -342,7 +356,7 @@ public function setRecordFieldsUpdateCallback($recordFieldsUpdateCallback) { $this->recordFieldsUpdateCallback = $recordFieldsUpdateCallback; return $this; } - + /** * @return callable|null */ diff --git a/src/Form/SortableUploadField.php b/src/Form/SortableUploadField.php index 32c38c1..25f3420 100644 --- a/src/Form/SortableUploadField.php +++ b/src/Form/SortableUploadField.php @@ -12,4 +12,25 @@ public function Field($properties = []) { \Requirements::css(SERIALIZED_DATAOBJECT_DIR . '/css/SortableUploadField.scss.css'); return parent::Field($properties); } + + /** + * @param array $value + * @param null $record + * @return \UploadField + * @throws \ValidationException + */ + public function setValue($value, $record = null) { + if (!empty($value['Files'])) { + // preserve sorting + $list = []; + foreach ($value['Files'] as $id) { + $file = \File::get()->byID($id); + if ($file && $file->exists()) { + $list[] = $file; + } + } + return parent::setValue(null, new \ArrayList($list)); + } + return parent::setValue($value, $record); + } } diff --git a/src/Form/UploadField.php b/src/Form/UploadField.php index a0a1e9a..8fb8b87 100644 --- a/src/Form/UploadField.php +++ b/src/Form/UploadField.php @@ -3,17 +3,12 @@ namespace zauberfisch\SerializedDataObject\Form; use SS_HTTPRequest; -use zauberfisch\SerializedDataObject\DataList; use zauberfisch\SerializedDataObject\DBField\DataListField; /** * @author Zauberfisch */ class UploadField extends \UploadField { - protected function getSerializableList($ids) { - return new DataList(\File::get()->byIDs($ids)->toArray()); - } - /** * @param \DataObjectInterface|\DataObject $record * @return $this @@ -24,34 +19,38 @@ public function saveInto(\DataObjectInterface $record) { return $this; } if ($record->hasField($fieldName)) { - $info = $record->db($fieldName); - if ($info == DataListField::class) { - // Get details to save - $value = $this->getSerializableList($this->getItemIDs()); - $dbValue = new DataListField(); - $dbValue->setValue($value, null, true); - $record->setField($fieldName, $dbValue); + /** @var DataListField $dbValue */ + $dbValue = $record->obj($fieldName); + $list = $dbValue->nullValue(); + foreach ($this->getItems() as $item) { + $list->add($item); } + $dbValue->setValue($list, null, true); + $record->setField($fieldName, $dbValue); } else { parent::saveInto($record); } return $this; } - + + /** + * @param array $value + * @param null $record + * @return \UploadField + * @throws \ValidationException + */ public function setValue($value, $record = null) { if (is_string($value) && $value) { - $dbField = new DataListField(); - $dbField->setValue($value, null, true); - $value = $dbField->getValue(); + $value = @unserialize($value); return parent::setValue(null, $value); } return parent::setValue($value, $record); } - + private static $allowed_actions = [ 'upload', ]; - + public function upload(SS_HTTPRequest $request) { $fieldName = $this->getName(); $baseStrLength = strpos($fieldName, '['); @@ -68,10 +67,9 @@ public function upload(SS_HTTPRequest $request) { ); } } - $return = parent::upload($request); - return $return; + return parent::upload($request); } - + protected function flattenFilesArray($array) { $fieldName = $this->getName(); $keys = substr($fieldName, strpos($fieldName, '[')); diff --git a/src/SortedDataList.php b/src/SortedDataList.php new file mode 100644 index 0000000..29581a9 --- /dev/null +++ b/src/SortedDataList.php @@ -0,0 +1,15 @@ +items = []; + foreach ($items as $item) { + $className = $item[0]; + $id = $item[1]; + $this->items[] = $className::get()->byID($id); + } + } +} \ No newline at end of file