diff --git a/code/GridFieldBulkActionDeleteHandler.php b/code/GridFieldBulkActionDeleteHandler.php new file mode 100644 index 0000000..bcc0e0d --- /dev/null +++ b/code/GridFieldBulkActionDeleteHandler.php @@ -0,0 +1,44 @@ + 'delete' + ); + + /** + * Delete the selected records passed from the delete bulk action + * + * @param SS_HTTPRequest $request + * @return SS_HTTPResponse List of deleted records ID + */ + public function delete(SS_HTTPRequest $request) + { + $ids = array(); + + foreach ( $this->getRecords() as $record ) + { + array_push($ids, $record->ID); + $record->delete(); + } + + $response = new SS_HTTPResponse(Convert::raw2json(array( + 'done' => true, + 'records' => $ids + ))); + $response->addHeader('Content-Type', 'text/json'); + return $response; + } +} \ No newline at end of file diff --git a/code/GridFieldBulkActionEditHandler.php b/code/GridFieldBulkActionEditHandler.php new file mode 100644 index 0000000..5e65175 --- /dev/null +++ b/code/GridFieldBulkActionEditHandler.php @@ -0,0 +1,152 @@ + '$Action' + 'bulkedit/update' => 'update', + 'bulkedit' => 'edit' + ); + + + /** + * Creates and return a Form + * with a collection of editable fields for each selected records + * + * @return Form Edit form with all the selected records + */ + public function edit() + { + $recordList = $this->getRecordIDList(); + + $crumbs = $this->Breadcrumbs(); + if($crumbs && $crumbs->count()>=2) $one_level_up = $crumbs->offsetGet($crumbs->count()-2); + + $actions = new FieldList(); + + $actions->push( + FormAction::create('SaveAll', _t('GridFieldBulkTools.SAVE_BTN_LABEL', 'Save All')) + ->setAttribute('id', 'bulkEditingUpdateBtn') + ->addExtraClass('ss-ui-action-constructive cms-panel-link') + ->setAttribute('data-icon', 'accept') + ->setAttribute('data-url', $this->gridField->Link('bulkaction/bulkedit/update')) + ->setUseButtonTag(true) + ); + + $actions->push( + FormAction::create('Cancel', _t('GridFieldBulkTools.CANCEL_BTN_LABEL', 'Cancel & Delete All')) + ->setAttribute('id', 'bulkEditingUpdateCancelBtn') + ->addExtraClass('ss-ui-action-destructive cms-panel-link') + ->setAttribute('data-icon', 'decline') + ->setAttribute('data-url', $this->Link('cancel')) + ->setUseButtonTag(true) + ); + + /* + * ******************************************************************** + */ + + $editedRecordList = new FieldList(); + $config = $this->component->getConfig(); + + foreach ( $recordList as $id ) + { + $recordCMSDataFields = GridFieldBulkEditingHelper::getModelCMSDataFields( $config, $this->gridField->list->dataClass ); + $recordCMSDataFields = GridFieldBulkEditingHelper::getModelFilteredDataFields($config, $recordCMSDataFields); + $recordCMSDataFields = GridFieldBulkEditingHelper::populateCMSDataFields( $recordCMSDataFields, $this->gridField->list->dataClass, $id ); + + $recordCMSDataFields['ID'] = new HiddenField('ID', '', $id); + $recordCMSDataFields = GridFieldBulkEditingHelper::escapeFormFieldsName( $recordCMSDataFields, $id ); + + $editedRecordList->push( + ToggleCompositeField::create( + 'GFBM_'.$id, + '#'.$id.': '.DataObject::get_by_id($this->gridField->list->dataClass, $id)->getTitle(), + array_values($recordCMSDataFields) + )->setHeadingLevel(4) + ->addExtraClass('bulkEditingFieldHolder') + ); + } + + /* + * ******************************************************************** + */ + + $form = new Form( + $this, + 'bulkEditingForm', + $editedRecordList, + $actions + ); + + $form->setTemplate('LeftAndMain_EditForm'); + $form->addExtraClass('center cms-content'); + $form->setAttribute('data-pjax-fragment', 'CurrentForm Content'); + + if($crumbs && $crumbs->count()>=2){ + $form->Backlink = $one_level_up->Link; + } + + $formHTML = $form->forTemplate(); + + Requirements::javascript(BULK_EDIT_TOOLS_PATH . '/javascript/GridFieldBulkManager.js'); + Requirements::css(BULK_EDIT_TOOLS_PATH . '/css/GridFieldBulkManager.css'); + Requirements::add_i18n_javascript(BULK_EDIT_TOOLS_PATH . '/javascript/lang'); + + $response = new SS_HTTPResponse($formHTML); + $response->addHeader('Content-Type', 'text/plain'); + $response->addHeader('X-Title', 'SilverStripe - Bulk '.$this->gridField->list->dataClass.' Editing'); + + if($this->request->isAjax()) + { + return $response; + } + else { + $controller = $this->getToplevelController(); + // If not requested by ajax, we need to render it within the controller context+template + return $controller->customise(array( + 'Content' => $response->getBody(), + )); + } + } + + + /** + * Saves the changes made in the bulk edit into the dataObject + * + * @return JSON + */ + public function update() + { + $data = GridFieldBulkEditingHelper::unescapeFormFieldsPOSTData($this->request->requestVars()); + $record = DataObject::get_by_id($this->gridField->list->dataClass, $data['ID']); + + foreach($data as $field => $value) + { + if ( $record->hasMethod($field) ) + { + $list = $record->$field(); + $list->setByIDList( $value ); + } + else{ + $record->setCastedField($field, $value); + } + } + $record->write(); + + return '{done:1,recordID:'.$data['ID'].'}'; + } +} \ No newline at end of file diff --git a/code/GridFieldBulkActionHandler.php b/code/GridFieldBulkActionHandler.php new file mode 100644 index 0000000..5b0c030 --- /dev/null +++ b/code/GridFieldBulkActionHandler.php @@ -0,0 +1,132 @@ +gridField = $gridField; + $this->component = $component; + $this->controller = $controller; + parent::__construct(); + } + + + /** + * Returns the URL for this RequestHandler + * + * @author SilverStripe + * @see GridFieldDetailForm_ItemRequest + * @param string $action + * @return string + */ + public function Link($action = null) + { + return Controller::join_links($this->gridField->Link(), 'bulkediting', $action); + } + + + /** + * Traverse up nested requests until we reach the first that's not a GridFieldDetailForm or GridFieldDetailForm_ItemRequest. + * The opposite of {@link Controller::curr()}, required because + * Controller::$controller_stack is not directly accessible. + * + * @return Controller + */ + protected function getToplevelController() + { + $c = $this->controller; + while($c && ($c instanceof GridFieldDetailForm_ItemRequest || $c instanceof GridFieldDetailForm)) { + $c = $c->getController(); + } + return $c; + } + + + /** + * Edited version of the GridFieldEditForm function + * adds the 'Bulk Upload' at the end of the crums + * + * CMS-specific functionality: Passes through navigation breadcrumbs + * to the template, and includes the currently edited record (if any). + * see {@link LeftAndMain->Breadcrumbs()} for details. + * + * @author SilverStripe original Breadcrumbs() method + * @see GridFieldDetailForm_ItemRequest + * @param boolean $unlinked + * @return ArrayData + */ + public function Breadcrumbs($unlinked = false) + { + if(!$this->controller->hasMethod('Breadcrumbs')) return; + + $items = $this->controller->Breadcrumbs($unlinked); + $items->push(new ArrayData(array( + 'Title' => 'Bulk Editing', + 'Link' => false + ))); + return $items; + } + + + /** + * Returns the list of record IDs selected in the front-end + * + * @return array List of IDs + */ + public function getRecordIDList() + { + $vars = $this->request->requestVars(); + return $vars['records']; + } + + + /** + * Returns a DataList of the records selected in the front-end + * + * @return DataList List of records + */ + public function getRecords() + { + $ids = $this->getRecordIDList(); + + if ( $ids ) + { + $class = $this->gridField->list->dataClass; + return DataList::create($class)->byIDs( $ids ); + } + else{ + return false; + } + } +} \ No newline at end of file diff --git a/code/GridFieldBulkActionUnlinkHandler.php b/code/GridFieldBulkActionUnlinkHandler.php new file mode 100644 index 0000000..f2a9324 --- /dev/null +++ b/code/GridFieldBulkActionUnlinkHandler.php @@ -0,0 +1,39 @@ + 'unlink' + ); + + /** + * Unlink the selected records passed from the unlink bulk action + * + * @param SS_HTTPRequest $request + * @return SS_HTTPResponse List of affected records ID + */ + public function unlink(SS_HTTPRequest $request) + { + $ids = $this->getRecordIDList(); + $this->gridField->list->removeMany($ids); + + $response = new SS_HTTPResponse(Convert::raw2json(array( + 'done' => true, + 'records' => $ids + ))); + $response->addHeader('Content-Type', 'text/json'); + return $response; + } +} \ No newline at end of file diff --git a/code/GridFieldBulkManager.php b/code/GridFieldBulkManager.php index 1827ac1..33ebbbb 100644 --- a/code/GridFieldBulkManager.php +++ b/code/GridFieldBulkManager.php @@ -5,8 +5,8 @@ * @author colymba * @package GridFieldBulkEditingTools */ -class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnProvider, GridField_URLHandler { - +class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnProvider, GridField_URLHandler +{ /** * component configuration * @@ -14,6 +14,7 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr * 'editableFields' => fields editable on the Model * 'fieldsClassBlacklist' => field types that will be removed from the automatic form generation * 'fieldsNameBlacklist' => fields that will be removed from the automatic form generation + * 'actions' => maps of action name and configuration * * @var array */ @@ -39,9 +40,9 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr if ( $defaultActions ) { $this->config['actions'] = array( - 'edit' => array( + 'bulkedit' => array( 'label' => _t('GridFieldBulkTools.EDIT_SELECT_LABEL', 'Edit'), - 'handler' => 'GridFieldBulkManager_Request', + 'handler' => 'GridFieldBulkActionEditHandler', 'config' => array( 'isAjax' => false, 'icon' => 'pencil', @@ -50,7 +51,7 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr ), 'unlink' => array( 'label' => _t('GridFieldBulkTools.UNLINK_SELECT_LABEL', 'UnLink'), - 'handler' => 'GridFieldBulkManager_Request', + 'handler' => 'GridFieldBulkActionUnlinkHandler', 'config' => array( 'isAjax' => true, 'icon' => 'chain--minus', @@ -59,7 +60,7 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr ), 'delete' => array( 'label' => _t('GridFieldBulkTools.DELETE_SELECT_LABEL', 'Delete'), - 'handler' => 'GridFieldBulkManager_Request', + 'handler' => 'GridFieldBulkActionDeleteHandler', 'config' => array( 'isAjax' => true, 'icon' => 'decline', @@ -346,22 +347,36 @@ class GridFieldBulkManager implements GridField_HTMLProvider, GridField_ColumnPr /** * Pass control over to the RequestHandler + * loop through the handlers provided in config['actions'] + * and find matching url_handlers. + * + * $url_handlers rule should not use wildcards like '$Action' => '$Action' + * but have more specific path defined * * @param GridField $gridField * @param SS_HTTPRequest $request * @return mixed */ public function handlebulkaction($gridField, $request) - { - $bulkAction = strtolower( $request->remaining() ); + { $controller = $gridField->getForm()->Controller(); - if ( isset($this->config['actions'][$bulkAction]) ) + foreach ($this->config['actions'] as $name => $data) { - $handlerClass = $this->config['actions'][$bulkAction]['handler']; - $handler = Injector::inst()->create($handlerClass, $gridField, $this, $controller); + $handlerClass = $data['handler']; + $urlHandlers = Config::inst()->get($handlerClass, 'url_handlers', Config::UNINHERITED); - return $handler->handleRequest($request, DataModel::inst()); + if($urlHandlers) foreach($urlHandlers as $rule => $action) + { + if($request->match($rule, false)) + { + //print_r('matched ' . $handlerClass . ' to ' . $rule); + $handler = Injector::inst()->create($handlerClass, $gridField, $this, $controller); + return $handler->handleRequest($request, DataModel::inst()); + } + } } + + user_error("Unable to find matching bulk action handler for ".$request->remaining().'.', E_USER_ERROR); } } \ No newline at end of file diff --git a/code/GridFieldBulkManager_Request.php b/code/GridFieldBulkManager_Request.php deleted file mode 100644 index 7538014..0000000 --- a/code/GridFieldBulkManager_Request.php +++ /dev/null @@ -1,282 +0,0 @@ - '$Action' - ); - - /** - * - * @param GridFIeld $gridField - * @param GridField_URLHandler $component - * @param Controller $controller - */ - public function __construct($gridField, $component, $controller) { - $this->gridField = $gridField; - $this->component = $component; - $this->controller = $controller; - parent::__construct(); - } - - /** - * Returns the URL for this RequestHandler - * - * @author SilverStripe - * @see GridFieldDetailForm_ItemRequest - * @param string $action - * @return string - */ - public function Link($action = null) { - return Controller::join_links($this->gridField->Link(), 'bulkediting', $action); - } - - - public function edit(SS_HTTPRequest $request) - { - $recordList = $this->getPOSTRecordList($request); - - $crumbs = $this->Breadcrumbs(); - if($crumbs && $crumbs->count()>=2) $one_level_up = $crumbs->offsetGet($crumbs->count()-2); - - $actions = new FieldList(); - - $actions->push( - FormAction::create('SaveAll', _t('GridFieldBulkTools.SAVE_BTN_LABEL', 'Save All')) - ->setAttribute('id', 'bulkEditingUpdateBtn') - ->addExtraClass('ss-ui-action-constructive cms-panel-link') - ->setAttribute('data-icon', 'accept') - ->setAttribute('data-url', $this->Link('update')) - ->setUseButtonTag(true) - ); - /* - if($crumbs && $crumbs->count()>=2) - { - $actions->push( - FormAction::create('SaveAndFinish', 'Save All & Finish') - ->setAttribute('id', 'bulkEditingUpdateFinishBtn') - ->addExtraClass('ss-ui-action-constructive cms-panel-link') - ->setAttribute('data-icon', 'accept') - ->setAttribute('data-url', $this->Link('update')) - ->setAttribute('data-return-url', $one_level_up->Link) - ->setUseButtonTag(true) - ); - } */ - - $actions->push( - FormAction::create('Cancel', _t('GridFieldBulkTools.CANCEL_BTN_LABEL', 'Cancel & Delete All')) - ->setAttribute('id', 'bulkEditingUpdateCancelBtn') - ->addExtraClass('ss-ui-action-destructive cms-panel-link') - ->setAttribute('data-icon', 'decline') - ->setAttribute('data-url', $this->Link('cancel')) - ->setUseButtonTag(true) - ); - - /* - * ******************************************************************** - */ - - $editedRecordList = new FieldList(); - $config = $this->component->getConfig(); - - foreach ( $recordList as $id ) - { - $recordCMSDataFields = GridFieldBulkEditingHelper::getModelCMSDataFields( $config, $this->gridField->list->dataClass ); - $recordCMSDataFields = GridFieldBulkEditingHelper::getModelFilteredDataFields($config, $recordCMSDataFields); - $recordCMSDataFields = GridFieldBulkEditingHelper::populateCMSDataFields( $recordCMSDataFields, $this->gridField->list->dataClass, $id ); - - $recordCMSDataFields['ID'] = new HiddenField('ID', '', $id); - $recordCMSDataFields = GridFieldBulkEditingHelper::escapeFormFieldsName( $recordCMSDataFields, $id ); - - $editedRecordList->push( - ToggleCompositeField::create( - 'GFBM_'.$id, - '#'.$id.': '.DataObject::get_by_id($this->gridField->list->dataClass, $id)->getTitle(), - array_values($recordCMSDataFields) - )->setHeadingLevel(4) - ->addExtraClass('bulkEditingFieldHolder') - ); - } - - /* - * ******************************************************************** - */ - - $form = new Form( - $this, - 'bulkEditingForm', - $editedRecordList, - $actions - ); - - $form->setTemplate('LeftAndMain_EditForm'); - $form->addExtraClass('center cms-content'); - $form->setAttribute('data-pjax-fragment', 'CurrentForm Content'); - - if($crumbs && $crumbs->count()>=2){ - $form->Backlink = $one_level_up->Link; - } - - $formHTML = $form->forTemplate(); - - Requirements::javascript(BULK_EDIT_TOOLS_PATH . '/javascript/GridFieldBulkManager.js'); - Requirements::css(BULK_EDIT_TOOLS_PATH . '/css/GridFieldBulkManager.css'); - Requirements::add_i18n_javascript(BULK_EDIT_TOOLS_PATH . '/javascript/lang'); - - $response = new SS_HTTPResponse($formHTML); - $response->addHeader('Content-Type', 'text/plain'); - $response->addHeader('X-Title', 'SilverStripe - Bulk '.$this->gridField->list->dataClass.' Editing'); - - if($request->isAjax()) - { - return $response; - } - else { - $controller = $this->getToplevelController(); - // If not requested by ajax, we need to render it within the controller context+template - return $controller->customise(array( - 'Content' => $response->getBody(), - )); - } - } - - /** - * Traverse up nested requests until we reach the first that's not a GridFieldDetailForm or GridFieldDetailForm_ItemRequest. - * The opposite of {@link Controller::curr()}, required because - * Controller::$controller_stack is not directly accessible. - * - * @return Controller - */ - protected function getToplevelController() { - $c = $this->controller; - while($c && ($c instanceof GridFieldDetailForm_ItemRequest || $c instanceof GridFieldDetailForm)) { - $c = $c->getController(); - } - return $c; - } - - /** - * Saves the changes made in the bulk edit into the dataObject - * - * @param SS_HTTPRequest $request - * @return JSON - */ - public function update(SS_HTTPRequest $request) - { - $data = GridFieldBulkEditingHelper::unescapeFormFieldsPOSTData($request->requestVars()); - $record = DataObject::get_by_id($this->gridField->list->dataClass, $data['ID']); - - foreach($data as $field => $value) - { - if ( $record->hasMethod($field) ) { - $list = $record->$field(); - $list->setByIDList( $value ); - }else{ - $record->setCastedField($field, $value); - } - } - $record->write(); - - return '{done:1,recordID:'.$data['ID'].'}'; - } - - /** - * - * @param SS_HTTPRequest $request - * @return \SS_HTTPResponse - */ - public function unlink(SS_HTTPRequest $request) - { - $recordList = $this->getPOSTRecordList($request); - $this->gridField->list->removeMany($recordList); - - $response = new SS_HTTPResponse(Convert::raw2json(array($recordList))); - $response->addHeader('Content-Type', 'text/plain'); - return $response; - } - - /** - * - * @param SS_HTTPRequest $request - * @return \SS_HTTPResponse - */ - public function delete(SS_HTTPRequest $request) - { - $recordList = $this->getPOSTRecordList($request); - $recordClass = $this->gridField->list->dataClass; - $result = array(); - - foreach ( $recordList as $id ) - { - $res = DataObject::delete_by_id($recordClass, $id); - array_push($result, array($id => $res)); - } - - $response = new SS_HTTPResponse(Convert::raw2json(array($result))); - $response->addHeader('Content-Type', 'text/plain'); - return $response; - } - - - public function getPOSTRecordList(SS_HTTPRequest $request) - { - $recordList = $request->requestVars(); - return $recordList['records']; - } - - /** - * Edited version of the GridFieldEditForm function - * adds the 'Bulk Upload' at the end of the crums - * - * CMS-specific functionality: Passes through navigation breadcrumbs - * to the template, and includes the currently edited record (if any). - * see {@link LeftAndMain->Breadcrumbs()} for details. - * - * @author SilverStripe original Breadcrumbs() method - * @see GridFieldDetailForm_ItemRequest - * @param boolean $unlinked - * @return ArrayData - */ - function Breadcrumbs($unlinked = false) { - if(!$this->controller->hasMethod('Breadcrumbs')) return; - - $items = $this->controller->Breadcrumbs($unlinked); - $items->push(new ArrayData(array( - 'Title' => 'Bulk Editing', - 'Link' => false - ))); - return $items; - } -} \ No newline at end of file