From 9289c69b1210fce47a72c6b714dcfd87b04ff64f Mon Sep 17 00:00:00 2001 From: Chris Tombleson Date: Thu, 22 Oct 2015 16:36:44 +1300 Subject: [PATCH] Bulk Manager - Bulk Add Handler --- .../code/GridFieldBulkActionAddHandler.php | 285 ++++++++++++++++++ bulkManager/code/GridFieldBulkManager.php | 103 ++++--- .../javascript/GridFieldBulkManager.js | 8 +- 3 files changed, 344 insertions(+), 52 deletions(-) create mode 100644 bulkManager/code/GridFieldBulkActionAddHandler.php diff --git a/bulkManager/code/GridFieldBulkActionAddHandler.php b/bulkManager/code/GridFieldBulkActionAddHandler.php new file mode 100644 index 0000000..0ece2a0 --- /dev/null +++ b/bulkManager/code/GridFieldBulkActionAddHandler.php @@ -0,0 +1,285 @@ + action map + * @var array + */ + private static $url_handlers = array( + 'bulkAdd/bulkAddForm' => 'bulkAddForm', + 'bulkAdd/recordAddForm' => 'recordAddForm', + 'bulkAdd' => 'index' + ); + + public function Link($action = null) + { + return Controller::join_links(parent::Link(), 'bulkAdd', $action); + } + + public function bulkAddForm() + { + $crumbs = $this->Breadcrumbs(); + + if ($crumbs && $crumbs->count()>=2) { + $one_level_up = $crumbs->offsetGet($crumbs->count()-2); + } + + $actions = new FieldList(); + + $actions->push( + FormAction::create('doSave', _t('GRIDFIELD_BULKMANAGER_ADD_HANDLER.SAVE_BTN_LABEL', 'Save all')) + ->setAttribute('id', 'bulkEditingSaveBtn') + ->addExtraClass('ss-ui-action-constructive') + ->setAttribute('data-icon', 'accept') + ->setUseButtonTag(true) + ); + + $actions->push( + FormAction::create('Cancel', _t('GRIDFIELD_BULKMANAGER_ADD_HANDLER.CANCEL_BTN_LABEL', 'Cancel')) + ->setAttribute('id', 'bulkEditingUpdateCancelBtn') + ->addExtraClass('ss-ui-action-destructive cms-panel-link') + ->setAttribute('data-icon', 'decline') + ->setAttribute('href', $one_level_up->Link) + ->setUseButtonTag(true) + ->setAttribute('src', '')//changes type to image so isn't hooked by default actions handlers + ); + + $recordsFieldList = new FieldList(); + + $modelClass = $this->gridField->getModelClass(); + $singleton = singleton($modelClass); + $titleModelClass = $singleton->i18n_singular_name(); + + //some cosmetics + $headerText = _t('GRIDFIELD_BULKMANAGER_ADD_HANDLER.HEADER_TEXT', + 'Adding {class}', + array( + 'class' => $titleModelClass + ) + ); + + $header = LiteralField::create( + 'bulkEditHeader', + '

' . $headerText . '

' + ); + + $recordsFieldList->push($header); + + $toggle = LiteralField::create( + 'bulkEditToggle', + '' . + _t('GRIDFIELD_BULKMANAGER_ADD_HANDLER.TOGGLE_ALL_LINK', 'Show/Hide all') . '' + ); + + $recordsFieldList->push($toggle); + + $record = new ListingImage(); + + for ($i=0; $i < 10; $i++) { + $tempForm = Form::create( + $this, "TempEditForm", + $record->getCMSFields(), + FieldList::create() + ); + + $fields = $tempForm->Fields(); + $fields = $this->filterRecordAddFields($fields, $i); + + $toggleField = ToggleCompositeField::create( + 'RecordFields_' . $i, + $record->getTitle(), + $fields + ) + ->setHeadingLevel(4) + ->setAttribute('data-id', $i) + ->addExtraClass('bulkEditingFieldHolder'); + + $recordsFieldList->push($toggleField); + } + + //recordEditForm name is here to trick SS to pass all subform request to recordEditForm() + $bulkEditForm = Form::create( + $this, + 'recordAddForm', + $recordsFieldList, + $actions + ); + + if ($crumbs && $crumbs->count()>=2) { + $bulkEditForm->Backlink = $one_level_up->Link; + } + + //override form action URL back to bulkEditForm + //and add record ids GET var + $bulkEditForm->setAttribute( + 'action', + $this->Link('bulkAddForm') + ); + + return $bulkEditForm; + } + + public function recordAddForm() + { + //clone current request : used to figure out what record we are asking + $request = clone $this->request; + $recordInfo = $request->shift(); + + //shift request till we find the requested field + while ($recordInfo) { + if ($unescapedRecordInfo = $this->unEscapeFieldName($recordInfo)) { + $id = $unescapedRecordInfo['id']; + $fieldName = $unescapedRecordInfo['name']; + $action = $request->shift(); + break; + } else { + $recordInfo = $request->shift(); + } + } + + //generate a form with only that requested record's fields + if ($id) { + $modelClass = $this->gridField->getModelClass(); + $record = DataObject::get_by_id($modelClass, $id); + + $cmsFields = $record->getCMSFields(); + $recordEditingFields = $this->getRecordEditingFields($record); + + return Form::create( + $this->gridField, + 'recordEditForm', + FieldList::create($recordEditingFields), + FieldList::create() + ); + } + } + + private function filterRecordAddFields(FieldList $fields, $num) + { + $config = $this->component->getConfig(); + $editableFields = $config['editableFields']; + + // get all dataFields or just the ones allowed in config + if ($editableFields){ + $dataFields = array(); + + foreach ($editableFields as $fieldName) { + $dataFields[$fieldName] = $fields->dataFieldByName($fieldName); + } + } else { + $dataFields = $fields->dataFields(); + } + + // escape field names with unique prefix + foreach ($dataFields as $name => $field) { + $field->Name = $this->escapeFieldName($num, $name); + $dataFields[$name] = $field; + } + + return $dataFields; + } + + protected function escapeFieldName($num, $name) + { + return 'record_' . $num . '_' . $name; + } + + protected function unEscapeFieldName($fieldName) + { + $parts = array(); + $match = preg_match('/record_(\d+)_(\w+)/i', $fieldName, $parts); + + if (!$match) { + return false; + } else { + return array( + 'id' => $parts[1], + 'name' => $parts[2], + ); + } + } + + public function index() + { + $form = $this->bulkAddForm(); + $form->setTemplate('LeftAndMain_EditForm'); + $form->addExtraClass('center cms-content'); + $form->setAttribute('data-pjax-fragment', 'CurrentForm Content'); + + Requirements::javascript(BULKEDITTOOLS_MANAGER_PATH . '/javascript/GridFieldBulkEditingForm.js'); + Requirements::css(BULKEDITTOOLS_MANAGER_PATH . '/css/GridFieldBulkEditingForm.css'); + Requirements::add_i18n_javascript(BULKEDITTOOLS_PATH . '/lang/js'); + + $controller = $this->getToplevelController(); + return $controller->customise(array( 'Content' => $form )); + } + + public function doSave($data, $form) + { + $className = $this->gridField->list->dataClass; + $singleton = singleton($className); + + $formsData = array(); + $done = 0; + + foreach ($data as $fieldName => $value) { + if ($fieldInfo = $this->unEscapeFieldName($fieldName)) { + if (!isset($formsData[$fieldInfo['id']])) { + $formsData[$fieldInfo['id']] = array(); + } + + $formsData[$fieldInfo['id']][$fieldInfo['name']] = $value; + } + } + + foreach ($formsData as $recordID => $recordData) { + if (!$this->isEmpty($recordData)) { + $record = new $className($recordData); + $id = $record->write(); + + if ($id) { + $this->gridField->list->add($record); + $done++; + } + } + } + + $messageModelClass = (($done > 1) ? $singleton->i18n_plural_name() : $singleton->i18n_singular_name()); + + $message = _t('GRIDFIELD_BULKMANAGER_ADD_HANDLER.SAVE_RESULT_TEXT', + '{count} {class} created successfully.', + array( + 'count' => $done, + 'class' => $messageModelClass + ) + ); + + $form->sessionMessage($message, 'good'); + + return Controller::curr()->redirect($this->Link()); + } + + private function isEmpty($data) + { + $empty = true; + $ignore_fields = array('SecurityID', 'Image'); + + foreach ($data as $name => $value) { + if (!in_array($name, $ignore_fields) && !empty($value)) { + $empty = false; + } + } + + return $empty; + } +} + diff --git a/bulkManager/code/GridFieldBulkManager.php b/bulkManager/code/GridFieldBulkManager.php index e8ecd47..f5e0d3b 100644 --- a/bulkManager/code/GridFieldBulkManager.php +++ b/bulkManager/code/GridFieldBulkManager.php @@ -17,8 +17,8 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr * @var array */ protected $config = array( - 'editableFields' => null, - 'actions' => array() + 'editableFields' => null, + 'actions' => array() ); @@ -29,39 +29,48 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr * @param boolean $defaultActions Use default actions list. False to start fresh. */ public function __construct($editableFields = null, $defaultActions = true) - { - if ( $editableFields != null ) $this->setConfig ( 'editableFields', $editableFields ); + { + if ($editableFields != null) $this->setConfig ('editableFields', $editableFields); - if ( $defaultActions ) + if ($defaultActions) { $this->config['actions'] = array( - 'bulkEdit' => array( - 'label' => _t('GRIDFIELD_BULK_MANAGER.EDIT_SELECT_LABEL', 'Edit'), - 'handler' => 'GridFieldBulkActionEditHandler', - 'config' => array( + 'bulkAdd' => array( + 'label' => _t('GRIDFIELD_BULK_MANAGER.ADD_SELECT_LABEL', 'Add'), + 'handler' => 'GridFieldBulkActionAddHandler', + 'config' => array( + 'isAjax' => false, + 'icon' => 'add', + 'isDestructive' => false + ) + ), + 'bulkEdit' => array( + 'label' => _t('GRIDFIELD_BULK_MANAGER.EDIT_SELECT_LABEL', 'Edit'), + 'handler' => 'GridFieldBulkActionEditHandler', + 'config' => array( 'isAjax' => false, 'icon' => 'pencil', 'isDestructive' => false ) - ), - 'unLink' => array( - 'label' => _t('GRIDFIELD_BULK_MANAGER.UNLINK_SELECT_LABEL', 'UnLink'), - 'handler' => 'GridFieldBulkActionUnLinkHandler', - 'config' => array( + ), + 'unLink' => array( + 'label' => _t('GRIDFIELD_BULK_MANAGER.UNLINK_SELECT_LABEL', 'UnLink'), + 'handler' => 'GridFieldBulkActionUnLinkHandler', + 'config' => array( 'isAjax' => true, 'icon' => 'chain--minus', 'isDestructive' => false ) - ), - 'delete' => array( - 'label' => _t('GRIDFIELD_BULK_MANAGER.DELETE_SELECT_LABEL', 'Delete'), - 'handler' => 'GridFieldBulkActionDeleteHandler', - 'config' => array( + ), + 'delete' => array( + 'label' => _t('GRIDFIELD_BULK_MANAGER.DELETE_SELECT_LABEL', 'Delete'), + 'handler' => 'GridFieldBulkActionDeleteHandler', + 'config' => array( 'isAjax' => true, 'icon' => 'decline', 'isDestructive' => true ) - ) + ) ); } } @@ -160,10 +169,10 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr } $this->config['actions'][$name] = array( - 'label' => $label, - 'handler' => $handler, - 'config' => $config - ); + 'label' => $label, + 'handler' => $handler, + 'config' => $config + ); return $this; } @@ -177,12 +186,11 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr */ function removeBulkAction($name) { - if ( !array_key_exists($name, $this->config['actions']) ) - { + if (!array_key_exists($name, $this->config['actions'])) { user_error("Bulk action '$name' doesn't exists.", E_USER_ERROR); } - unset( $this->config['actions'][$name] ); + unset($this->config['actions'][$name]); return $this; } @@ -228,8 +236,8 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr function getColumnContent($gridField, $record, $columnName) { $cb = CheckboxField::create('bulkSelect_'.$record->ID) - ->addExtraClass('bulkSelect no-change-track') - ->setAttribute('data-record', $record->ID); + ->addExtraClass('bulkSelect no-change-track') + ->setAttribute('data-record', $record->ID); return $cb->Field(); } @@ -263,7 +271,6 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr } - /* ********************************************************************** * GridField_HTMLProvider * */ @@ -279,18 +286,16 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr Requirements::javascript(BULKEDITTOOLS_MANAGER_PATH . '/javascript/GridFieldBulkManager.js'); Requirements::add_i18n_javascript(BULKEDITTOOLS_PATH . '/lang/js'); - if ( !count($this->config['actions']) ) - { + if (!count($this->config['actions'])) { user_error("Trying to use GridFieldBulkManager without any bulk action.", E_USER_ERROR); } $actionsListSource = array(); $actionsConfig = array(); - foreach ($this->config['actions'] as $action => $actionData) - { - $actionsListSource[$action] = $actionData['label']; - $actionsConfig[$action] = $actionData['config']; + foreach ($this->config['actions'] as $action => $actionData) { + $actionsListSource[$action] = $actionData['label']; + $actionsConfig[$action] = $actionData['config']; } reset($this->config['actions']); @@ -301,19 +306,19 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr ->setAttribute('class', 'bulkActionName no-change-track') ->setAttribute('id', ''); - $templateData = array( - 'Menu' => $dropDownActionsList->FieldHolder(), - 'Button' => array( - 'Label' => _t('GRIDFIELD_BULK_MANAGER.ACTION_BTN_LABEL', 'Go'), - 'DataURL' => $gridField->Link('bulkAction'), - 'Icon' => $this->config['actions'][$firstAction]['config']['icon'], - 'DataConfig' => htmlspecialchars(json_encode($actionsConfig), ENT_QUOTES, 'UTF-8') - ), - 'Select' => array( - 'Label' => _t('GRIDFIELD_BULK_MANAGER.SELECT_ALL_LABEL', 'Select all') - ), - 'Colspan' => (count($gridField->getColumns()) - 1) - ); + $templateData = array( + 'Menu' => $dropDownActionsList->FieldHolder(), + 'Button' => array( + 'Label' => _t('GRIDFIELD_BULK_MANAGER.ACTION_BTN_LABEL', 'Go'), + 'DataURL' => $gridField->Link('bulkAction'), + 'Icon' => $this->config['actions'][$firstAction]['config']['icon'], + 'DataConfig' => htmlspecialchars(json_encode($actionsConfig), ENT_QUOTES, 'UTF-8') + ), + 'Select' => array( + 'Label' => _t('GRIDFIELD_BULK_MANAGER.SELECT_ALL_LABEL', 'Select all') + ), + 'Colspan' => (count($gridField->getColumns()) - 1) + ); $templateData = new ArrayData($templateData); diff --git a/bulkManager/javascript/GridFieldBulkManager.js b/bulkManager/javascript/GridFieldBulkManager.js index 14329b8..c92cfc7 100644 --- a/bulkManager/javascript/GridFieldBulkManager.js +++ b/bulkManager/javascript/GridFieldBulkManager.js @@ -14,6 +14,7 @@ targets = ['.filter-header', '.sortable-header'], $target = $parent.find(targets.join(',')), + $component = this.clone(), index = $tr.index(this), newIndex = $tr.length - 1 ; @@ -28,8 +29,9 @@ if ( index > newIndex ) { - $tr.eq(newIndex).insertAfter($(this)); - } + $component.insertBefore($tr.eq(newIndex)); + this.remove(); + } }, onunmatch: function(){} }); @@ -191,7 +193,7 @@ data = { records: ids } ; - if ( ids.length <= 0 ) + if ( ids.length <= 0 && action != 'bulkAdd' ) { alert( ss.i18n._t('GRIDFIELD_BULK_MANAGER.BULKACTION_EMPTY_SELECT') ); return;