SilverStripe: How to add a checkbox column to a gridfield

From FVue
Jump to: navigation, search

Class:Fixed Problems

Problem

I have a GridField showing a list of reactions and I want to select multiple records and do either a delete or publish on this selection.

How to add a column with checkboxes to a GridField and process them?

Environment

  • SilverStripe-3.0

Solution

See also: (SOLVED) Checkboxes In GridField

Ascii-art

 +--------------------------+                 +--------------------------+
 |       «interface»        |                 |       «interface»        |
 | GridField_ColumnProvider |                 | GridField_ActionProvider |
 +------------+-------------+                 +-------------+------------+
             /_\                                           /_\
              :                                             :
              :                             ................;................
              :                             :                               :
 +------------+----------+     +------------+------------+     +------------+-------------+
 | GridFieldSelectColumn |     | GridFieldSelectedDelete |     | GridFieldSelectedPublish |
 +-----------------------+     +------------+------------+     +------------+-------------+
                                           /_\                             /_\
                                            |                               |
                                            |                               |          
                              +-------------+------------+    +-------------+-------------+ 
                              | GridFieldReactionsDelete |    | GridFieldReactionsPublish | 
                              +--------------------------+    +---------------------------+ 

Code

NOTE: The CSS-class no-change-track prevents the browser from asking the question below if you made a selection and want to navigate away from the page:

Are you sure? // This page is asking you to confirm that you want to leave - data you have entered may not be saved. // Stay on Page Leave Page
Index: articles/templates/GridFieldSelectColumn.ss
===================================================================
--- articles/templates/GridFieldSelectColumn.ss	(revision 0)
+++ articles/templates/GridFieldSelectColumn.ss	(revision 271)
@@ -0,0 +1 @@
+<input id="item_$ID" class="checkbox no-change-track" name="selected[]" type="checkbox" value="$ID" />
 
 
Index: articles/javascript/lang/en_US.js
===================================================================
--- articles/javascript/lang/en_US.js	(revision 0)
+++ articles/javascript/lang/en_US.js	(revision 272)
@@ -0,
0 +1,8 @@
+if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
+    if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
+} else {
+    ss.i18n.addDictionary('en_US', {
+        'GRIDFIELD.DELETEMULTIPLECONFIRMMESSAGE': 'Delete selected records(s)?',
+        'GRIDFIELD.PUBLISHMULTIPLECONFIRMMESSAGE': 'Publish selected records(s)?',
+    });
+}
 
 
Index: articles/javascript/lang/nl_NL.js
===================================================================
--- articles/javascript/lang/nl_NL.js	(revision 0)
+++ articles/javascript/lang/nl_NL.js	(revision 271)
@@ -0,0 +1,4 @@
+ss.i18n.addDictionary('nl_NL', {
+    'GRIDFIELD.DELETEMULTIPLECONFIRMMESSAGE': 'Verwijderen geselecteerde records(s)?',
+    'GRIDFIELD.PUBLISHMULTIPLECONFIRMMESSAGE': 'Publiceren geselecteerde records(s)?',
+});
 
 
Index: articles/javascript/ReactionsAdmin_GridField.js
===================================================================
--- articles/javascript/ReactionsAdmin_GridField.js	(revision 0)
+++ articles/javascript/ReactionsAdmin_GridField.js	(revision 271)
@@ -0,0 +1,40 @@
+(function($){
+    $(document).ready(function() {
+        $.entwine('ss', function($) {
+            $('.ss-gridfield-item[data-class="Reaction"]').entwine({
+                onclick: function(e) {
+                    // This prevents clicking a checkbox from jumping to item view
+                    return true;
+                },
+                onmouseover: function() {
+                    return false;
+                },
+                onmouseout: function() {
+                    return false;
+                }
+            });
+
+            $('#action_deleteSelected').entwine({
+                onclick: function(e){
+				    if(!confirm(ss.i18n._t('GRIDFIELD.DELETEMULTIPLECONFIRMMESSAGE'))) {
+                        e.preventDefault();
+                        return false;
+                    } else {
+                        this._super(e);
+                    }
+                }
+            });
+
+            $('#action_publishSelected').entwine({
+                onclick: function(e){
+				    if(!confirm(ss.i18n._t('GRIDFIELD.PUBLISHMULTIPLECONFIRMMESSAGE'))) {
+                        e.preventDefault();
+                        return false;
+                    } else {
+                        this._super(e);
+                    }
+                }
+            });
+        });
+    });
+}(jQuery));
 
 
Index: articles/code/GridFieldReactionsDelete.php
===================================================================
--- articles/code/GridFieldReactionsDelete.php	(revision 0)
+++ articles/code/GridFieldReactionsDelete.php	(revision 271)
@@ -0,0 +1,18 @@
+<?php
+/**
+ * @package framework
+ * @subpackage gridfield
+ */
+
+/**
+ * Does the actual deletion of GridField selected records
+ */
+class GridFieldReactionsDelete extends GridFieldSelectedDelete {
+    /**
+     * Implement ancestor method.
+     * @see GridFieldDeleteSelected::deleteRecord()
+     */
+    protected function deleteRecord($id) {
+        Reaction::get()->byID($id)->delete();
+    }
+}
 
 
Index: articles/code/GridFieldSelectedDelete.php
===================================================================
--- articles/code/GridFieldSelectedDelete.php	(revision 0)
+++ articles/code/GridFieldSelectedDelete.php	(revision 271)
@@ -0,0 +1,77 @@
+<?php
+/**
+ * @package framework
+ * @subpackage gridfield
+ */
+
+/**
+ * Adds a "Delete" button to the bottom of a GridField.
+ */
+class GridFieldSelectedDelete implements GridField_HTMLProvider, GridField_ActionProvider {
+	/**
+	 * Fragment to write the button to
+	 */
+	protected $targetFragment;
+
+
+	/**
+	 * @param string $targetFragment The HTML fragment to write the button into
+	 */
+	public function __construct($targetFragment = "after") {
+		$this->targetFragment = $targetFragment;
+	}
+
+
+	/**
+	 * Place the export button in a <p> tag below the field
+	 */
+	public function getHTMLFragments($gridField) {
+		$button = new GridField_FormAction(
+			$gridField, 
+			'deleteSelected', 
+			'Verwijderen',
+			'deleteSelected', 
+			null
+		);
+		$button->setAttribute('data-icon', 'delete');
+		$button->addExtraClass('no-ajax');
+		return array(
+			$this->targetFragment => '<span class="grid-delete-button">' . $button->Field() . '</span>',
+		);
+	}
+
+
+	/**
+	 * "Delete" is an action button
+	 */
+	public function getActions($gridField) {
+		return array('deleteSelected');
+	}
+
+	public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
+		if ($actionName == 'deleteselected') {
+			$this->deleteRecords($gridField, $data);
+            $gridField->form->getController()->redirectBack();
+		}
+	}
+
+
+	/**
+	 * Handle the delete
+ 	 */
+	public function deleteRecords($gridField, $request = null) {
+        if (is_array($request) && isset($request['selected'])) {
+            $ids = $request['selected'];
+            foreach ($ids as $id) {
+                $this->deleteRecord($id);
+            }
+        }
+	}
+
+
+    /**
+     * To be implemented by descendant.
+     */
+    protected function deleteRecord($id) {
+    }
+}
 
 
Index: articles/code/GridFieldReactionsPublish.php
===================================================================
--- articles/code/GridFieldReactionsPublish.php	(revision 0)
+++ articles/code/GridFieldReactionsPublish.php	(revision 271)
@@ -0,0 +1,20 @@
+<?php
+/**
+ * @package framework
+ * @subpackage gridfield
+ */
+
+/**
+ * Does the actual publish of GridField selected records
+ */
+class GridFieldReactionsPublish extends GridFieldSelectedPublish {
+    /**
+     * Implement ancestor method.
+     * @see GridFieldDeleteSelected::deleteRecord()
+     */
+    protected function publishRecord($id) {
+        $reaction = Reaction::get()->byID($id);
+        $reaction->IsPublished = 1;
+        $reaction->write();
+    }
+}
 
 
Index: articles/code/GridFieldSelectedPublish.php
===================================================================
--- articles/code/GridFieldSelectedPublish.php	(revision 0)
+++ articles/code/GridFieldSelectedPublish.php	(revision 271)
@@ -0,0 +1,77 @@
+<?php
+/**
+ * @package framework
+ * @subpackage gridfield
+ */
+
+/**
+ * Adds a "Publish" button to the bottom of a GridField.
+ */
+class GridFieldSelectedPublish implements GridField_HTMLProvider, GridField_ActionProvider {
+	/**
+	 * Fragment to write the button to
+	 */
+	protected $targetFragment;
+
+
+	/**
+	 * @param string $targetFragment The HTML fragment to write the button into
+	 */
+	public function __construct($targetFragment = "after") {
+		$this->targetFragment = $targetFragment;
+	}
+
+
+	/**
+	 * Place the export button in a <p> tag below the field
+	 */
+	public function getHTMLFragments($gridField) {
+		$button = new GridField_FormAction(
+			$gridField, 
+			'publishSelected', 
+			'Publiceren',
+			'publishSelected', 
+			null
+		);
+		$button->setAttribute('data-icon', 'network-cloud');
+		$button->addExtraClass('no-ajax');
+		return array(
+			$this->targetFragment => '<span class="grid-publish-button">' . $button->Field() . '</span>',
+		);
+	}
+
+
+	/**
+	 * "Publish" is an action button
+	 */
+	public function getActions($gridField) {
+		return array('publishSelected');
+	}
+
+	public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
+		if ($actionName == 'publishselected') {
+			$this->publishRecords($gridField, $data);
+            $gridField->form->getController()->redirectBack();
+		}
+	}
+
+
+    /**
+	 * Handle the publish
+ 	 */
+	public function publishRecords($gridField, $request = null) {
+        if (is_array($request) && isset($request['selected'])) {
+            $ids = $request['selected'];
+            foreach ($ids as $id) {
+                $this->publishRecord($id);
+            }
+        }
+	}
+
+
+    /**
+     * To be implemented by descendant.
+     */
+    protected function publishRecord($id) {
+    }
+}
 
 
Index: articles/code/ReactionsAdminExtension.php
===================================================================
--- articles/code/ReactionsAdminExtension.php	(revision 0)
+++ articles/code/ReactionsAdminExtension.php	(revision 271)
@@ -0,0 +1,19 @@
+<?php
+class ReactionsAdminExtension extends Extension {
+    function updateEditForm($form) {
+        $fieldList = $form->Fields();
+        
+        foreach($fieldList as $field) {
+            if ($field instanceof GridField) {
+                $config = $field->getConfig();
+                $config->getComponentByType('GridFieldDataColumns')->setFieldFormatting(            
+                    array(
+                        'Body' => '<a href=\"' . $this->owner->Link() . '/EditForm/field/Reaction/item/$ID/edit\">$value</a>',
+                    )
+                );
+                $field->setConfig($config);
+            }
+
+        } 
+    }
+}
 
 
Index: articles/code/ReactionsAdmin.php
===================================================================
--- articles/code/ReactionsAdmin.php	(revision 268)
+++ articles/code/ReactionsAdmin.php	(revision 271)
@@ -18,148 +18,23 @@
 
     public $showImportForm = false;
 
+    public function getEditForm($id = null, $fields = null) {
+        $form = parent::getEditForm($id, $fields);
+        $gridfield = $form->fields->items[0];
+        $gridfield->config->addComponent(new GridFieldSelectColumn, $insert_before = 'GridFieldDataColumns');
+        $gridfield->config->addComponent(new GridFieldReactionsDelete);
+        $gridfield->config->addComponent(new GridFieldReactionsPublish);
+        return $form;
+    }
+
+
+	public function init() {
+		parent::init();
+		Requirements::javascript('articles/javascript/ReactionsAdmin_GridField.js');
+		Requirements::add_i18n_javascript('articles/javascript/lang');
+	}
 }
 
 
Index: articles/code/GridFieldSelectColumn.php
===================================================================
--- articles/code/GridFieldSelectColumn.php	(revision 0)
+++ articles/code/GridFieldSelectColumn.php	(revision 271)
@@ -0,0 +1,33 @@
+<?php
+
+class GridFieldSelectColumn implements GridField_ColumnProvider {
+   public function __construct($useToggle = true, $targetFragment = 'before') {
+      $this->targetFragment = $targetFragment;
+      $this->useToggle = $useToggle;
+   }
+   
+   public function augmentColumns($field, &$cols) {
+      if(!in_array('Select', $cols)) $cols[] = 'Select';
+   }
+   
+   public function getColumnsHandled($field) {
+      return array('Select');
+   }
+   
+   public function getColumnContent($field, $record, $col) {
+      if($record->canView()) {
+         $data = new ArrayData(array(
+            'ID' => $record->ID
+         ));
+         return $data->renderWith('GridFieldSelectColumn');
+      }
+   }
+   
+   public function getColumnAttributes($field, $record, $col) {
+      return array('class' => 'col-select');
+   }
+
+   public function getColumnMetadata($gridField, $col) {
+      return array('title' => "");
+   }
+}
 
 
Index: articles/code/ArticlesAdmin.php
===================================================================
--- articles/code/ArticlesAdmin.php	(revision 268)
+++ articles/code/ArticlesAdmin.php	(revision 271)
@@ -35,6 +35,7 @@
 	public function init() {
 		parent::init();
         Requirements::javascript('articles/javascript/ArticlesAdmin_GridField.js');
+		Requirements::add_i18n_javascript('articles/javascript/lang');
         Requirements::css('articles/css/ArticlesAdmin-GridField.css');
 	}