├── images ├── ContentBlock.png ├── ContentBlockTwo.png ├── blockedit.svg └── BlockIcons.svg ├── _config └── config.yml ├── templates └── GridField_EditableBlockRow.ss ├── composer.json ├── code ├── extensions │ ├── BlockEnhancements_SiteTreeExt.php │ └── UploadFieldExtension.php ├── controllers │ └── LeftAndMain_BlockActions.php └── forms │ ├── GF_BlockEnhancements.php │ ├── GridfieldConfig_BlockManager_bu.txt │ ├── GridfieldConfig_BlockManager_buv2.txt │ ├── GFConf_BlockManagerEnhanced.php │ └── EditableBlockRow.php ├── LICENSE.txt ├── README.md ├── css ├── BlockEnhancements.css └── EditableBlockRow.css ├── _config.php └── js ├── BlockEnhancements.js ├── display_logic_editablerow-fixes.js └── EditableBlockRow.js /images/ContentBlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/restruct/silverstripe-block_enhancements/HEAD/images/ContentBlock.png -------------------------------------------------------------------------------- /images/ContentBlockTwo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/restruct/silverstripe-block_enhancements/HEAD/images/ContentBlockTwo.png -------------------------------------------------------------------------------- /_config/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | Name: 'blockenhancements' 3 | After: 4 | - 'blocks' 5 | - 'gridfieldextensions' 6 | --- 7 | GridFieldAddNewMultiClass: 8 | showEmptyString: false 9 | 10 | Injector: 11 | GridFieldConfig_BlockManager: 12 | class: GFConf_BlockManagerEnhanced 13 | 14 | UploadField: 15 | extensions: 16 | - 'Milkyway\SS\Core\Extensions\UploadFieldExtension' 17 | 18 | # add 'publish with blocks' action to page 19 | Page: 20 | extensions: 21 | - BlockEnhancements_SiteTreeExt 22 | LeftAndMain: 23 | extensions: 24 | - LeftAndMain_BlockActions -------------------------------------------------------------------------------- /templates/GridField_EditableBlockRow.ss: -------------------------------------------------------------------------------- 1 |
' + data.text + ''
38 | );
39 | return $state;
40 | };
41 | // apply select2 (set inline width if in gridfield to get consistens results)
42 | if(this.parents('.ss-gridfield').length){
43 | this.css('width','100%');
44 | }
45 | this.select2({
46 | templateSelection: optionHtmlTemplate,
47 | templateResult: optionHtmlTemplate,
48 | minimumResultsForSearch: 'Infinity'
49 | });
50 | //this.select2();
51 | }
52 |
53 | });
54 |
55 | $('.ss-gridfield select.select2blocktype').entwine({
56 |
57 | // update blocks on change (in Gridfield)
58 | onchange: function(){
59 |
60 | // duplicated from GridFieldOrderableRows.onadd.update:
61 | var grid = this.getGridField();
62 | var data = [];
63 | data.push({
64 | name: 'block_id',
65 | value: this.closest('tr').data("id")
66 | });
67 | data.push({
68 | name: 'block_type',
69 | value: this.val()
70 | });
71 |
72 | // area-assignment forwards the request to gridfieldextensions::reorder server side
73 | grid.reload({
74 | //url: grid.data("url-reorder"),
75 | url: grid.data("url-blocktype-assignment"),
76 | data: data
77 | });
78 | }
79 |
80 | });
81 |
82 | });
83 | })(jQuery);
84 |
--------------------------------------------------------------------------------
/js/display_logic_editablerow-fixes.js:
--------------------------------------------------------------------------------
1 | // This patch applies some fixes to displaylogic to make it work with EditableRows,
2 | // probably also fixes deep updates with dot notation in fieldnames.
3 | // These edits could probably included in the general display_logic, as they mainly make the selectors
4 | // less rigid, because we have no way of knowing the exact IDs & names on beforehand for every situation
5 | (function($) {
6 |
7 | // make selectors more specific to override default display logic behaviour
8 | $('.ss-gridfield-blockenhancements div.display-logic, .ss-gridfield-blockenhancements div.display-logic-master')
9 | .entwine({
10 |
11 | findHolder: function(name) {
12 | // Again, don't search exact ID as we have way of knowing that, check for ends with instead
13 | return this.closest('form,fieldset').find('[id$='+name+'_Holder]');
14 | },
15 |
16 | getFormField: function() {
17 | // displaylogicwrappers conain the eval & masters on themselves instead of the child fields
18 | if(this.hasClass('displaylogicwrapper')){
19 | return this;
20 | }
21 | // leave out the actual name, just return the first element with a name attribute CONTAINING the name,
22 | // as we have no way of knowing the exact name because of deep editing
23 | return this.find('[name*='+name+']');
24 | //return this.find('[name]');
25 | //return this.find('[name='+name+']');
26 | },
27 |
28 | onmatch: function () {
29 |
30 | var allReadonly = true;
31 | var masters = [];
32 | var field = this.getFormField();
33 | if(field.data('display-logic-eval') && field.data('display-logic-masters')) {
34 | this.data('display-logic-eval', field.data('display-logic-eval'))
35 | .data('display-logic-masters', field.data('display-logic-masters'));
36 | }
37 |
38 | masters = this.getMasters();
39 |
40 | for(m in masters) {
41 | var holderName = this.nameToHolder(masters[m]);
42 |
43 | // again, search for field ending with MasterName_Holder, and limit to current fieldset as
44 | // multiple identical fieldsets may be included on the same form only with different IDs
45 | //var master = this.closest('form').find(this.escapeSelector('#'+holderName));
46 | master = this.closest('form,fieldset').find('[id$='+masters[m]+'_Holder]');
47 | // Continue with regular code:
48 |
49 | if(!master.is('.readonly')) allReadonly = false;
50 |
51 | master.addClass("display-logic-master");
52 | if(master.find('input[type=radio]').length) {
53 | master.addClass('optionset');
54 | }
55 | if(master.find("input[type=checkbox]").length > 1) {
56 | master.addClass('checkboxset');
57 | }
58 | }
59 |
60 | // If all the masters are readonly fields, the field has no way of displaying.
61 | if(masters.length && allReadonly) {
62 | this.show();
63 | }
64 |
65 | // Bubble up to super methods if both master & listener (needed for ao Switch field)
66 | if(this.hasClass('display-logic-hidden') && this.hasClass('display-logic-master')) {
67 | this._super();
68 | }
69 |
70 | }
71 |
72 | });
73 |
74 | // make selectors more specific to override default display logic behaviour
75 | $('.ss-gridfield-blockenhancements div.display-logic-master').entwine({
76 | Listeners: null,
77 |
78 | getListeners: function() {
79 | if(l = this._super()) {
80 | return l;
81 | }
82 | var self = this;
83 | var listeners = [];
84 | // EDIT
85 | this.closest("form,fieldset").find('.display-logic').each(function() {
86 | masters = $(this).getMasters();
87 | for(m in masters) {
88 | //if(self.nameToHolder(masters[m]) == self.attr('id')) {
89 | var endswith = new RegExp(masters[m]+'_Holder$');
90 | if (self.attr('id').match(endswith)!=null){
91 | // END:EDIT
92 | listeners.push($(this)[0]);
93 | break;
94 | }
95 | }
96 | });
97 | this.setListeners(listeners);
98 | return this.getListeners();
99 | }
100 | });
101 |
102 | })(jQuery);
103 |
--------------------------------------------------------------------------------
/code/controllers/LeftAndMain_BlockActions.php:
--------------------------------------------------------------------------------
1 | owner->getResponseNegotiator()->respond($this->owner->request);
17 | // }
18 |
19 | // Example from leftandmain.php:
20 | // public function save($data, $form) {
21 | // $className = $this->stat('tree_class');
22 | //
23 | // // Existing or new record?
24 | // $id = $data['ID'];
25 | // if(substr($id,0,3) != 'new') {
26 | // $record = DataObject::get_by_id($className, $id);
27 | // if($record && !$record->canEdit()) return Security::permissionFailure($this);
28 | // if(!$record || !$record->ID) $this->httpError(404, "Bad record ID #" . (int)$id);
29 | // } else {
30 | // if(!singleton($this->stat('tree_class'))->canCreate()) return Security::permissionFailure($this);
31 | // $record = $this->getNewItem($id, false);
32 | // }
33 | //
34 | // // save form data into record
35 | // $form->saveInto($record, true);
36 | // $record->write();
37 | // $this->extend('onAfterSave', $record);
38 | // $this->setCurrentPageID($record->ID);
39 | //
40 | // $this->getResponse()->addHeader('X-Status', rawurlencode(_t('LeftAndMain.SAVEDUP', 'Saved.')));
41 | // return $this->getResponseNegotiator()->respond($this->getRequest());
42 | // }
43 |
44 | public function publishPageAndBlocks($data, $form)
45 | {
46 |
47 | // regular save
48 | $this->owner->save($data, $form);
49 |
50 | // Now publish the whole bunch
51 | if ( $page = SiteTree::get()->byID($data['ID']) ) {
52 | if (!$page->canPublish()) {
53 | throw new SS_HTTPResponse_Exception("Publish page not allowed", 403);
54 | }
55 | // else: publish page (also triggers editable columns/rows write())
56 | $page->doPublish();
57 |
58 | // and publish any blocks which the user's allowed to publish (have already been written)
59 | if (is_callable(array($page, 'Blocks'))) {
60 | foreach ($page->Blocks() as $block){
61 | // skip any blocks that we cannot publish
62 | if (!$block->canPublish()) continue;
63 | // publish
64 | $block->invokeWithExtensions('onBeforePublish', $block);
65 | $block->publish('Stage', 'Live');
66 | $block->invokeWithExtensions('onAfterPublish', $block);
67 | }
68 | }
69 | } else {
70 | throw new SS_HTTPResponse_Exception(
71 | "Bad page ID #" . (int)$data['ID'], 404);
72 | }
73 |
74 | // this generates a message that will show up in the CMS
75 | $this->owner->response->addHeader(
76 | 'X-Status',
77 | rawurlencode("Page + blocks published")
78 | );
79 |
80 | return $this->owner->getResponseNegotiator()->respond($this->owner->request);
81 | }
82 |
83 | // public function doAction($data, $form){
84 | // $className = $this->owner->stat('tree_class');
85 | // $SQL_id = Convert::raw2sql($data['ID']);
86 | //
87 | // $record = DataObject::get_by_id($className, $SQL_id);
88 | //
89 | // if(!$record || !$record->ID){
90 | // throw new SS_HTTPResponse_Exception(
91 | // "Bad record ID #" . (int)$data['ID'], 404);
92 | // }
93 | //
94 | // // at this point you have a $record,
95 | // // which is your page you can work with!
96 | //
97 | // // this generates a message that will show up in the CMS
98 | // $this->owner->response->addHeader(
99 | // 'X-Status',
100 | // rawurlencode('Success message!')
101 | // );
102 | //
103 | // return $this->owner->getResponseNegotiator()
104 | // ->respond($this->owner->request);
105 | // }
106 |
107 | }
--------------------------------------------------------------------------------
/code/forms/GF_BlockEnhancements.php:
--------------------------------------------------------------------------------
1 | ViewableData::create()->ThemeDir(),
21 | // "ThemeDir" => SSViewer::get_theme_folder(),
22 | // "ProjectDir" => project(),
23 | // "AreaNoneTitle" => Config::inst()->get(get_class(), 'unassigned_area_description'),
24 | //// "BlockAreas" => json_encode( $blockAreas )
25 | // );
26 | //->setAttribute('data-project-dir', project());
27 | // Requirements::javascriptTemplate($moduleDir.'/js/BlockEnhancements.js', $jsVars, 'BlockEnhancements');
28 | Requirements::javascript($moduleDir.'/js/BlockEnhancements.js');
29 | Requirements::css($moduleDir.'/css/BlockEnhancements.css');
30 |
31 | Requirements::javascript($moduleDir.'/js/EditableBlockRow.js');
32 | Requirements::css($moduleDir.'/css/EditableBlockRow.css');
33 |
34 | Requirements::javascript($moduleDir.'/js/display_logic_editablerow-fixes.js');
35 | }
36 |
37 | public function getURLHandlers($grid) {
38 | return array(
39 | // 'POST area_assignment' => 'handleAreaAssignment',
40 | 'POST blocktype_assignment' => 'handleBlockTypeAssignment',
41 | );
42 | }
43 |
44 | // public function __construct()
45 | // {
46 | // parent::__construct();
47 | // self::include_requirements();
48 | // }
49 |
50 | /**
51 | * @param GridField $field
52 | */
53 | public function getHTMLFragments($field) {
54 |
55 | self::include_requirements();
56 |
57 | // set ajax urls / vars
58 | $field->addExtraClass('ss-gridfield-blockenhancements');
59 | // $field->setAttribute('data-url-area-assignment', $field->Link('area_assignment'));
60 | $field->setAttribute('data-url-blocktype-assignment', $field->Link('blocktype_assignment'));
61 | // $field->setAttribute('data-block-area-none-title', Config::inst()->get(get_class(), 'unassigned_area_description'));
62 |
63 | // add no-chozen to dropdown
64 | // $field->getConfig()->getComponentByType('GridFieldAddNewMultiClass')->
65 | // $field->getConfig()->getComponentByType('GridFieldDetailForm')->setAttribute('data-project-dir', project());
66 |
67 | }
68 |
69 | /**
70 | * Handles requests to assign a new block area to a block item
71 | *
72 | * @param GridField $grid
73 | * @param SS_HTTPRequest $request
74 | * @return SS_HTTPResponse
75 | */
76 | // public function handleAreaAssignment($grid, $request) {
77 | // $list = $grid->getList();
78 | //
79 | // // @TODO: do we need this? (copied from GridFieldOrderableRows::handleReorder)
80 | //// $modelClass = $grid->getModelClass();
81 | //// if ($list instanceof ManyManyList && !singleton($modelClass)->canView()) {
82 | //// $this->httpError(403);
83 | //// } else if(!($list instanceof ManyManyList) && !singleton($modelClass)->canEdit()) {
84 | //// $this->httpError(403);
85 | //// }
86 | //
87 | // $blockid = $request->postVar('blockarea_block_id');
88 | // $blockarea = $request->postVar('blockarea_area');
89 | // if($blockarea=='none') $blockarea = '';
90 | // $block = $list->byID($blockid);
91 | //
92 | // // Update item with correct Area assigned (custom query required to write m_m_extraField)
93 | //// $block->BlockArea = $blockarea;
94 | //// $block->write();
95 | // // @TODO: improve this custom query to be more robust?
96 | // DB::query(sprintf(
97 | // "UPDATE `%s` SET `%s` = '%s' WHERE `BlockID` = %d",
98 | // 'SiteTree_Blocks',
99 | // 'BlockArea',
100 | // $blockarea,
101 | // $blockid
102 | // ));
103 | //
104 | // // Forward the request to GridFieldOrderableRows::handleReorder
105 | // return $grid->getConfig()
106 | // ->getComponentByType('GridFieldOrderableRows')
107 | // ->handleReorder($grid, $request);
108 | // }
109 |
110 | /**
111 | * Handles requests to assign a new block area to a block item
112 | *
113 | * @param GridField $grid
114 | * @param SS_HTTPRequest $request
115 | * @return SS_HTTPResponse
116 | */
117 | public function handleBlockTypeAssignment($grid, $request) {
118 | $list = $grid->getList();
119 |
120 | // @TODO: do we need this? (copied from GridFieldOrderableRows::handleReorder)
121 | // $modelClass = $grid->getModelClass();
122 | // if ($list instanceof ManyManyList && !singleton($modelClass)->canView()) {
123 | // $this->httpError(403);
124 | // } else if(!($list instanceof ManyManyList) && !singleton($modelClass)->canEdit()) {
125 | // $this->httpError(403);
126 | // }
127 |
128 | $blockid = $request->postVar('block_id');
129 | $blocktype = $request->postVar('block_type');
130 | $block = $list->byID($blockid);
131 |
132 | // Update item with correct Area assigned (custom query required to write m_m_extraField)
133 | $block->ClassName = $blocktype;
134 | $block->write();
135 | //print_r($block->record);
136 | // // @TODO: improve this custom query to be more robust?
137 | // DB::query(sprintf(
138 | // "UPDATE `%s` SET `%s` = '%s' WHERE `BlockID` = %d",
139 | // 'SiteTree_Blocks',
140 | // 'BlockArea',
141 | // $blockarea,
142 | // $blockid
143 | // ));
144 | return $grid->FieldHolder();
145 |
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/code/forms/GridfieldConfig_BlockManager_bu.txt:
--------------------------------------------------------------------------------
1 |
7 | */
8 | class GridFieldConfig_BlockManager extends GridFieldConfig
9 | {
10 | public $blockManager;
11 |
12 | public function __construct($canAdd = true, $canEdit = true, $canDelete = true, $editableRows = false, $aboveOrBelow = false)
13 | {
14 | parent::__construct();
15 |
16 | $this->blockManager = Injector::inst()->get('BlockManager');
17 | $controllerClass = Controller::curr()->class;
18 |
19 | // Get available Areas (for page) or all in case of ModelAdmin
20 | if ($controllerClass == 'CMSPageEditController') {
21 | $currentPage = Controller::curr()->currentPage();
22 | $areasFieldSource = $this->blockManager->getAreasForPageType($currentPage->ClassName);
23 | } else {
24 | $areasFieldSource = $this->blockManager->getAreasForTheme();
25 | }
26 | $blockTypeArray = $this->blockManager->getBlockClasses();
27 |
28 | // EditableColumns only makes sense on Saveable parenst (eg Page), or inline changes won't be saved
29 | if ($editableRows) {
30 | $displayfields = array(
31 | //'singular_name' => array('title' => _t('Block.BlockType', 'Block Type'), 'field' => 'ReadonlyField'),
32 | 'ClassName' => array(
33 | 'title' => _t('Block.BlockType', 'Block Type').'
34 | ',
35 | // the s prevent wrapping of dropdowns
36 | 'callback' => function () use ($blockTypeArray) {
37 | return DropdownField::create('ClassName', 'Block Type', $blockTypeArray)
38 | // ->setHasEmptyDefault(true)
39 | ->addExtraClass('select2blocktype');
40 | },
41 | ),
42 | 'Title' => array(
43 | 'title' => _t('Block.TitleName', 'Block Name'),
44 | // 'field' => 'ReadonlyField'
45 | 'field' => 'TextField'
46 | ),
47 | 'BlockArea' => array(
48 | 'title' => _t('Block.BlockArea', 'Block Area').'
49 | ',
50 | // the s prevent wrapping of dropdowns
51 | 'callback' => function () use ($areasFieldSource) {
52 | return DropdownField::create('BlockArea', 'Block Area', $areasFieldSource)
53 | ->setHasEmptyDefault(true);
54 | },
55 | ),
56 | 'isPublishedNice' => array('title' => _t('Block.IsPublishedField', 'Published'), 'field' => 'ReadonlyField'),
57 | 'UsageListAsString' => array('title' => _t('Block.UsageListAsString', 'Used on'), 'field' => 'ReadonlyField'),
58 | );
59 |
60 | if ($aboveOrBelow) {
61 | $displayfields['AboveOrBelow'] = array(
62 | 'title' => _t('GridFieldConfigBlockManager.AboveOrBelow', 'Above or Below'),
63 | 'callback' => function () {
64 | return DropdownField::create('AboveOrBelow', _t('GridFieldConfigBlockManager.AboveOrBelow', 'Above or Below'), BlockSet::config()->get('above_or_below_options'));
65 | },
66 | );
67 | }
68 | $this->addComponent($editable = new GridFieldEditableColumns());
69 | $editable->setDisplayFields($displayfields);
70 | $this->addComponent($erow = new EditableBlockRow());
71 | // $erow->setFields('getEditableBlockRowFields');
72 | } else { // BlockManager
73 | $this->addComponent($dcols = new GridFieldDataColumns());
74 |
75 | $displayfields = array(
76 | 'singular_name' => _t('Block.BlockType', 'Block Type'),
77 | // 'Title' => _t('Block.Title', 'Description'),
78 | 'Title' => _t('Block.TitleName', 'Block Name'),
79 | 'BlockArea' => _t('Block.BlockArea', 'Block Area'),
80 | 'isPublishedNice' => _t('Block.IsPublishedField', 'Published'),
81 | 'UsageListAsString' => _t('Block.UsageListAsString', 'Used on'),
82 | );
83 | $dcols->setDisplayFields($displayfields);
84 | $dcols->setFieldCasting(array('UsageListAsString' => 'HTMLText->Raw'));
85 |
86 | // optionally add copybutton
87 | if(class_exists('GridFieldCopyButton')){
88 | $this->addComponent(new GridFieldCopyButton());
89 | }
90 | }
91 |
92 | $this->addComponent(new GridFieldButtonRow('before'));
93 | $this->addComponent(new GridFieldButtonRow('after'));
94 | $this->addComponent(new GridFieldToolbarHeader());
95 | $this->addComponent(new GridFieldDetailForm());
96 | $this->addComponent(new GridFieldDetailForm());
97 |
98 |
99 | // load enhancements module
100 | if(class_exists('GF_BlockEnhancements')){
101 | $this->addComponent(new GF_BlockEnhancements());
102 | }
103 |
104 | // stuff only for BlockAdmin
105 | if ($controllerClass == 'BlockAdmin') {
106 | $this->addComponent($sort = new GridFieldSortableHeader());
107 | $sort->setThrowExceptionOnBadDataType(false);
108 | $this->addComponent($filter = new GridFieldFilterHeader());
109 | $filter->setThrowExceptionOnBadDataType(false);
110 | } else {
111 | // only for GF on SiteTree
112 | $this->addComponent(new GridFieldTitleHeader());
113 | $this->addComponent(new GridFieldFooter());
114 | }
115 |
116 | if ($canAdd) {
117 | $multiClass = new GridFieldAddNewMultiClass('after');
118 | $classes = $this->blockManager->getBlockClasses();
119 | $multiClass->setClasses($classes);
120 | $this->addComponent($multiClass);
121 | //$this->addComponent(new GridFieldAddNewButton());
122 | }
123 |
124 | if ($canEdit) {
125 | $this->addComponent(new GridFieldEditButton());
126 | }
127 |
128 | if ($canDelete) {
129 | $this->addComponent(new GridFieldDeleteAction(true));
130 | }
131 |
132 | return $this;
133 | }
134 |
135 | /**
136 | * Add the GridFieldAddExistingSearchButton component to this grid config.
137 | *
138 | * @return $this
139 | **/
140 | public function addExisting()
141 | {
142 | $this->addComponent($add = new GridFieldAddExistingSearchButton('buttons-after-right'));
143 | $add->setSearchList(Block::get());
144 |
145 | return $this;
146 | }
147 |
148 | /**
149 | * Add the GridFieldBulkManager component to this grid config.
150 | *
151 | * @return $this
152 | **/
153 | public function addBulkEditing()
154 | {
155 | if (class_exists('GridFieldBulkManager')) {
156 | $this->addComponent(new GridFieldBulkManager());
157 | }
158 |
159 | return $this;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/code/forms/GridfieldConfig_BlockManager_buv2.txt:
--------------------------------------------------------------------------------
1 |
7 | */
8 | class GridFieldConfig_BlockManager extends GridFieldConfig
9 | {
10 | public $blockManager;
11 |
12 | public function __construct($canAdd = true, $canEdit = true, $canDelete = true, $editableRows = false, $aboveOrBelow = false)
13 | {
14 | parent::__construct();
15 |
16 | $this->blockManager = Injector::inst()->get('BlockManager');
17 | $controllerClass = Controller::curr()->class;
18 | // Get available Areas (for page) or all in case of ModelAdmin
19 | if ($controllerClass == 'CMSPageEditController') {
20 | $currentPage = Controller::curr()->currentPage();
21 | $areasFieldSource = $this->blockManager->getAreasForPageType($currentPage->ClassName);
22 | } else {
23 | $areasFieldSource = $this->blockManager->getAreasForTheme();
24 | }
25 | // EDIT
26 | $blockTypeArray = $this->blockManager->getBlockClasses();
27 | // /EDIT
28 |
29 | // EditableColumns only makes sense on Saveable parenst (eg Page), or inline changes won't be saved
30 | if ($editableRows) {
31 | $this->addComponent($editable = new GridFieldEditableColumns());
32 | $displayfields = array(
33 | // EDIT
34 | //'singular_name' => array('title' => _t('Block.BlockType', 'Block Type'), 'field' => 'ReadonlyField'),
35 | 'ClassName' => array(
36 | 'title' => _t('Block.BlockType', 'Block Type').'
37 | ',
38 | // the s prevent wrapping of dropdowns
39 | 'callback' => function () use ($blockTypeArray) {
40 | return DropdownField::create('ClassName', 'Block Type', $blockTypeArray)
41 | // ->setHasEmptyDefault(true)
42 | ->addExtraClass('select2blocktype');
43 | },
44 | ),
45 | 'Title' => array(
46 | 'title' => _t('Block.TitleName', 'Block Name'),
47 | // 'field' => 'ReadonlyField'
48 | 'field' => 'TextField'
49 | ),
50 | // /EDIT
51 | 'BlockArea' => array(
52 | 'title' => _t('Block.BlockArea', 'Block Area').'
53 | ',
54 | // the s prevent wrapping of dropdowns
55 | 'callback' => function () use ($areasFieldSource) {
56 | return DropdownField::create('BlockArea', 'Block Area', $areasFieldSource)
57 | ->setHasEmptyDefault(true);
58 | },
59 | ),
60 | 'isPublishedNice' => array('title' => _t('Block.IsPublishedField', 'Published'), 'field' => 'ReadonlyField'),
61 | 'UsageListAsString' => array('title' => _t('Block.UsageListAsString', 'Used on'), 'field' => 'ReadonlyField'),
62 | );
63 |
64 | if ($aboveOrBelow) {
65 | $displayfields['AboveOrBelow'] = array(
66 | 'title' => _t('GridFieldConfigBlockManager.AboveOrBelow', 'Above or Below'),
67 | 'callback' => function () {
68 | return DropdownField::create('AboveOrBelow', _t('GridFieldConfigBlockManager.AboveOrBelow', 'Above or Below'), BlockSet::config()->get('above_or_below_options'));
69 | },
70 | );
71 | }
72 | $editable->setDisplayFields($displayfields);
73 | // EDIT
74 | $this->addComponent($erow = new EditableBlockRow());
75 | // /EDIT
76 | } else {
77 | $this->addComponent($dcols = new GridFieldDataColumns());
78 |
79 | $displayfields = array(
80 | 'singular_name' => _t('Block.BlockType', 'Block Type'),
81 | // EDIT
82 | // 'Title' => _t('Block.Title', 'Description'),
83 | 'Title' => _t('Block.TitleName', 'Block Name'),
84 | // /EDIT
85 | 'BlockArea' => _t('Block.BlockArea', 'Block Area'),
86 | 'isPublishedNice' => _t('Block.IsPublishedField', 'Published'),
87 | 'UsageListAsString' => _t('Block.UsageListAsString', 'Used on'),
88 | );
89 | $dcols->setDisplayFields($displayfields);
90 | $dcols->setFieldCasting(array('UsageListAsString' => 'HTMLText->Raw'));
91 | }
92 |
93 | $this->addComponent(new GridFieldButtonRow('before'));
94 | // EDIT
95 | $this->addComponent(new GridFieldButtonRow('after'));
96 | // /EDIT
97 | $this->addComponent(new GridFieldToolbarHeader());
98 | $this->addComponent(new GridFieldDetailForm());
99 | // EDIT
100 | //$this->addComponent($sort = new GridFieldSortableHeader());
101 | //$this->addComponent($filter = new GridFieldFilterHeader());
102 | //$this->addComponent(new GridFieldDetailForm());
103 |
104 | //$filter->setThrowExceptionOnBadDataType(false);
105 | //$sort->setThrowExceptionOnBadDataType(false);
106 |
107 | // load enhancements module
108 | if(class_exists('GF_BlockEnhancements')){
109 | $this->addComponent(new GF_BlockEnhancements());
110 | }
111 |
112 | // stuff only for BlockAdmin
113 | if ($controllerClass == 'BlockAdmin') {
114 | $this->addComponent($sort = new GridFieldSortableHeader());
115 | $sort->setThrowExceptionOnBadDataType(false);
116 | $this->addComponent($filter = new GridFieldFilterHeader());
117 | $filter->setThrowExceptionOnBadDataType(false);
118 | } else {
119 | // only for GF on SiteTree
120 | $this->addComponent(new GridFieldTitleHeader());
121 | $this->addComponent(new GridFieldFooter());
122 | }
123 |
124 | if ($canAdd) {
125 | $multiClass = new GridFieldAddNewMultiClass('after');
126 | $classes = $this->blockManager->getBlockClasses();
127 | $multiClass->setClasses($classes);
128 | $this->addComponent($multiClass);
129 | //$this->addComponent(new GridFieldAddNewButton());
130 | }
131 | // /EDIT
132 |
133 | if ($controllerClass == 'BlockAdmin' && class_exists('GridFieldCopyButton')) {
134 | $this->addComponent(new GridFieldCopyButton());
135 | }
136 |
137 | if ($canEdit) {
138 | $this->addComponent(new GridFieldEditButton());
139 | }
140 |
141 | if ($canDelete) {
142 | $this->addComponent(new GridFieldDeleteAction(true));
143 | }
144 |
145 | return $this;
146 | }
147 |
148 | /**
149 | * Add the GridFieldAddExistingSearchButton component to this grid config.
150 | *
151 | * @return $this
152 | **/
153 | public function addExisting()
154 | {
155 | // EDIT
156 | //$this->addComponent($add = new GridFieldAddExistingSearchButton());
157 | $this->addComponent($add = new GridFieldAddExistingSearchButton('buttons-after-right'));
158 | // /EDIT
159 | $add->setSearchList(Block::get());
160 |
161 | return $this;
162 | }
163 |
164 | /**
165 | * Add the GridFieldBulkManager component to this grid config.
166 | *
167 | * @return $this
168 | **/
169 | public function addBulkEditing()
170 | {
171 | if (class_exists('GridFieldBulkManager')) {
172 | $this->addComponent(new GridFieldBulkManager());
173 | }
174 |
175 | return $this;
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/images/BlockIcons.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
155 |
--------------------------------------------------------------------------------
/code/forms/GFConf_BlockManagerEnhanced.php:
--------------------------------------------------------------------------------
1 | blockManager = Injector::inst()->get('BlockManager');
16 | $controllerClass = Controller::curr()->class;
17 | // Get available Areas (for page) or all in case of ModelAdmin
18 | if ($controllerClass == 'CMSPageEditController') {
19 | $currentPage = Controller::curr()->currentPage();
20 | $areasFieldSource = $this->blockManager->getAreasForPageType($currentPage->ClassName);
21 | } else {
22 | $areasFieldSource = $this->blockManager->getAreasForTheme();
23 | }
24 | // EDIT
25 | $blockTypeArray = $this->blockManager->getBlockClasses();
26 | // /EDIT
27 |
28 | // EditableColumns only makes sense on Saveable parenst (eg Page), or inline changes won't be saved
29 | if ($editableRows) {
30 |
31 | // set project-dir in cookie to be accessible as fallback from js
32 | Cookie::set('js-project-dir',project(),90,null,null,false,false);
33 |
34 | $this->addComponent($editable = new GridFieldEditableColumns());
35 | $displayfields = array(
36 | // EDIT
37 | //'singular_name' => array('title' => _t('Block.BlockType', 'Block Type'), 'field' => 'ReadonlyField'),
38 | 'ClassName' => array(
39 | 'title' => _t('Block.BlockType', 'Block Type').'
40 | ',
41 | // the s prevent wrapping of dropdowns
42 | 'callback' => function () use ($blockTypeArray) {
43 |
44 | return DropdownField::create('ClassName', 'Block Type', $blockTypeArray)
45 | // ->setHasEmptyDefault(true)
46 | ->addExtraClass('select2blocktype')
47 | ->setAttribute('data-project-dir', project());
48 | },
49 | ),
50 | 'Title' => array(
51 | 'title' => _t('Block.TitleName', 'Block Name'),
52 | // 'field' => 'ReadonlyField'
53 | 'field' => 'TextField'
54 | ),
55 | // /EDIT
56 | 'BlockArea' => array(
57 | 'title' => _t('Block.BlockArea', 'Block Area').'
58 | ',
59 | // the s prevent wrapping of dropdowns
60 | 'callback' => function () use ($areasFieldSource) {
61 | return DropdownField::create('BlockArea', 'Block Area', $areasFieldSource)
62 | ->setHasEmptyDefault(true);
63 | },
64 | ),
65 | 'isPublishedNice' => array('title' => _t('Block.IsPublishedField', 'Published'), 'field' => 'ReadonlyField'),
66 | 'UsageListAsString' => array('title' => _t('Block.UsageListAsString', 'Used on'), 'field' => 'ReadonlyField'),
67 | );
68 |
69 | if ($aboveOrBelow) {
70 | $displayfields['AboveOrBelow'] = array(
71 | 'title' => _t('GridFieldConfigBlockManager.AboveOrBelow', 'Above or Below'),
72 | 'callback' => function () {
73 | return DropdownField::create('AboveOrBelow', _t('GridFieldConfigBlockManager.AboveOrBelow', 'Above or Below'), BlockSet::config()->get('above_or_below_options'));
74 | },
75 | );
76 | }
77 | $editable->setDisplayFields($displayfields);
78 | // EDIT
79 | $this->addComponent($erow = new EditableBlockRow());
80 | // /EDIT
81 | } else {
82 | $this->addComponent($dcols = new GridFieldDataColumns());
83 |
84 | $displayfields = array(
85 | 'singular_name' => _t('Block.BlockType', 'Block Type'),
86 | // EDIT
87 | // 'Title' => _t('Block.Title', 'Description'),
88 | 'Title' => _t('Block.TitleName', 'Block Name'),
89 | // /EDIT
90 | 'BlockArea' => _t('Block.BlockArea', 'Block Area'),
91 | 'isPublishedNice' => _t('Block.IsPublishedField', 'Published'),
92 | 'UsageListAsString' => _t('Block.UsageListAsString', 'Used on'),
93 | );
94 | $dcols->setDisplayFields($displayfields);
95 | $dcols->setFieldCasting(array('UsageListAsString' => 'HTMLText->Raw'));
96 | }
97 |
98 | $this->addComponent(new GridFieldButtonRow('before'));
99 | // EDIT
100 | $this->addComponent(new GridFieldButtonRow('after'));
101 | // /EDIT
102 | $this->addComponent(new GridFieldToolbarHeader());
103 | $this->addComponent(new GridFieldDetailForm());
104 | // EDIT
105 | //$this->addComponent($sort = new GridFieldSortableHeader());
106 | //$this->addComponent($filter = new GridFieldFilterHeader());
107 | //$this->addComponent(new GridFieldDetailForm());
108 |
109 | //$filter->setThrowExceptionOnBadDataType(false);
110 | //$sort->setThrowExceptionOnBadDataType(false);
111 |
112 | // load enhancements module (eg inline editing etc, needs save action @TODO: move to SiteTree only?
113 | if(class_exists('GF_BlockEnhancements')){
114 | $this->addComponent(new GF_BlockEnhancements());
115 | }
116 |
117 | // stuff only for BlockAdmin
118 | if ($controllerClass == 'BlockAdmin') {
119 | $this->addComponent($sort = new GridFieldSortableHeader());
120 | $sort->setThrowExceptionOnBadDataType(false);
121 | $this->addComponent($filter = new GridFieldFilterHeader());
122 | $filter->setThrowExceptionOnBadDataType(false);
123 | } else {
124 | // only for GF on SiteTree
125 | $this->addComponent(new GridFieldTitleHeader());
126 | $this->addComponent(new GridFieldFooter());
127 | // groupable
128 | $this->addComponent(new GridFieldGroupable(
129 | 'BlockArea',
130 | 'Area',
131 | 'none',
132 | $areasFieldSource
133 | ));
134 | // var_dump($areasFieldSource);
135 |
136 | // // Get available Areas (for page) enhancements inactive when in ModelAdmin/BlockAdmin
137 | // if (Controller::curr() && Controller::curr()->class == 'CMSPageEditController') {
138 | // // Provide defined blockAreas to JS
139 | // $blockManager = Injector::inst()->get('BlockManager');
140 | //// $blockAreas = $blockManager->getAreasForPageType( Controller::curr()->currentPage()->ClassName );
141 | // $blockAreas = $blockManager->getAreasForPageType( Controller::curr()->currentPage()->ClassName );
142 | // }
143 | }
144 |
145 | if ($canAdd) {
146 | $multiClass = new GridFieldAddNewMultiClass('after');
147 | $classes = $this->blockManager->getBlockClasses();
148 | $multiClass->setClasses($classes);
149 | $this->addComponent($multiClass);
150 | //$this->addComponent(new GridFieldAddNewButton());
151 | }
152 | // /EDIT
153 |
154 | if ($controllerClass == 'BlockAdmin' && class_exists('GridFieldCopyButton')) {
155 | $this->addComponent(new GridFieldCopyButton());
156 | }
157 |
158 | if ($canEdit) {
159 | $this->addComponent(new GridFieldEditButton());
160 | }
161 |
162 | if ($canDelete) {
163 | $this->addComponent(new GridFieldDeleteAction(true));
164 | }
165 |
166 | return $this;
167 | }
168 |
169 | /**
170 | * Add the GridFieldAddExistingSearchButton component to this grid config.
171 | *
172 | * @return $this
173 | **/
174 | public function addExisting()
175 | {
176 | // EDIT
177 | //$this->addComponent($add = new GridFieldAddExistingSearchButton());
178 | $this->addComponent($add = new GridFieldAddExistingSearchButton('buttons-after-right'));
179 | // /EDIT
180 | $add->setSearchList(Block::get());
181 |
182 | return $this;
183 | }
184 |
185 | /**
186 | * Add the GridFieldBulkManager component to this grid config.
187 | *
188 | * @return $this
189 | **/
190 | public function addBulkEditing()
191 | {
192 | if (class_exists('GridFieldBulkManager')) {
193 | $this->addComponent(new GridFieldBulkManager());
194 | }
195 |
196 | return $this;
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/js/EditableBlockRow.js:
--------------------------------------------------------------------------------
1 | (function ($) {
2 | $.entwine("ss", function ($) {
3 |
4 | // A global method on grid fields to display the no items message if no rows are found
5 | $(".ss-gridfield").entwine({
6 | showNoItemsMessage: function () {
7 | if (this.find('.ss-gridfield-items:first').children().not('.ss-gridfield-no-items').length === 0) {
8 | this.find('.ss-gridfield-no-items').show();
9 | }
10 | }
11 | });
12 |
13 | // Milkyway\SS\GridFieldUtils\EditableRow
14 | $('.cms-container').entwine({
15 | OpenGridFieldToggles: {},
16 | saveTabState: function () {
17 | var $that = this,
18 | OpenGridFieldToggles = $that.getOpenGridFieldToggles();
19 |
20 | $that._super();
21 |
22 | $that.find('.ss-gridfield.ss-gridfield-editable-rows').each(function () {
23 | var $this = $(this),
24 | openToggles = $this.getOpenToggles();
25 |
26 | if (openToggles.length) {
27 | OpenGridFieldToggles[$this.attr('id')] = $this.getOpenToggles();
28 | }
29 | });
30 | },
31 | restoreTabState: function (overrideStates) {
32 | var $that = this,
33 | OpenGridFieldToggles = $that.getOpenGridFieldToggles();
34 |
35 | $that._super(overrideStates);
36 |
37 | $.each(OpenGridFieldToggles, function (id, openToggles) {
38 | $that.find('#' + id + '.ss-gridfield.ss-gridfield-editable-rows').reopenToggles(openToggles);
39 | });
40 |
41 | $that.find('.ss-gridfield-editable-row--toggle_start').click();
42 |
43 | $that.setOpenGridFieldToggles({});
44 | }
45 | });
46 |
47 | $(".ss-gridfield.ss-gridfield-editable-rows").entwine({
48 | reload: function (opts, success) {
49 | var $grid = this,
50 | openToggles = $grid.getOpenToggles(),
51 | args = arguments;
52 |
53 | this._super(opts, function () {
54 | $grid.reopenToggles(openToggles);
55 | $grid.find('.ss-gridfield-editable-row--toggle_start').click();
56 |
57 | if (success) {
58 | success.apply($grid, args);
59 | }
60 | });
61 | },
62 | getOpenToggles: function () {
63 | var $grid = this,
64 | openToggles = [];
65 |
66 | if ($grid.hasClass('ss-gridfield-editable-rows_disableToggleState')) {
67 | return openToggles;
68 | }
69 |
70 | $grid.find(".ss-gridfield-editable-row--toggle_open").each(function (key) {
71 | var $this = $(this),
72 | $holder = $this.parents('td:first'),
73 | $parent = $this.parents('tr:first'),
74 | $currentGrid = $parent.parents('.ss-gridfield:first'),
75 | $editable = $parent.next();
76 |
77 | if (!$editable.hasClass('ss-gridfield-editable-row--row')
78 | || $editable.data('id') != $parent.data('id')
79 | || $editable.data('class') != $parent.data('class')) {
80 | $editable = null;
81 | }
82 | else if ($currentGrid.hasClass('ss-gridfield-editable-rows_disableToggleState')) {
83 | return true;
84 | }
85 |
86 | openToggles[key] = {
87 | link: $holder.data('link')
88 | };
89 |
90 | if ($editable) {
91 | $editable.find('.ss-tabset.ui-tabs').each(function () {
92 | if (!openToggles[key].tabs) {
93 | openToggles[key].tabs = {};
94 | }
95 |
96 | openToggles[key].tabs[this.id] = $(this).tabs('option', 'selected');
97 | });
98 |
99 | if ($currentGrid.hasClass('ss-gridfield-editable-rows_allowCachedToggles')) {
100 | openToggles[key].row = $editable.detach();
101 | }
102 | }
103 | });
104 |
105 | return openToggles;
106 | },
107 | reopenToggles: function (openToggles) {
108 | var $grid = this,
109 | openTabsInToggle = function (currentToggle, $row) {
110 | if (currentToggle.hasOwnProperty('tabs') && currentToggle.tabs) {
111 | $.each(currentToggle.tabs, function (key, value) {
112 | $row.find('#' + key + '.ss-tabset.ui-tabs').tabs({
113 | active: value
114 | });
115 | });
116 | }
117 | };
118 |
119 | if ($grid.hasClass('ss-gridfield-editable-rows_disableToggleState')) {
120 | return;
121 | }
122 |
123 | $.each(openToggles, function (key) {
124 | if (openToggles[key].hasOwnProperty('link') && openToggles[key].link) {
125 | var $toggleHolder = $grid.find("td.ss-gridfield-editable-row--icon-holder[data-link='"
126 | + openToggles[key].link + "']");
127 |
128 | if (!$toggleHolder.length) {
129 | return true;
130 | }
131 | }
132 | else {
133 | return true;
134 | }
135 |
136 | if (openToggles[key].hasOwnProperty('row') && openToggles[key].row) {
137 | var $parent = $toggleHolder.parents('tr:first');
138 |
139 | $toggleHolder
140 | .find(".ss-gridfield-editable-row--toggle")
141 | .addClass('ss-gridfield-editable-row--toggle_loaded ss-gridfield-editable-row--toggle_open');
142 |
143 | if (!$parent.next().hasClass('ss-gridfield-editable-row--row')) {
144 | $parent.after(openToggles[key].row);
145 | }
146 | }
147 | else if (openToggles[key].hasOwnProperty('link') && openToggles[key].link) {
148 | $toggleHolder.find(".ss-gridfield-editable-row--toggle").trigger('click', function ($newRow) {
149 | $grid.find('.ss-gridfield.ss-gridfield-editable-rows').reopenToggles(openToggles);
150 | openTabsInToggle(openToggles[key], $newRow);
151 | }, false);
152 | }
153 | });
154 | }
155 | });
156 |
157 | $(".ss-gridfield-editable-row--toggle").entwine({
158 | onclick: function (e, callback, noFocus) {
159 | var $this = this,
160 | $holder = $this.parents('td:first'),
161 | link = $holder.data('link'),
162 | $parent = $this.parents('tr:first');
163 |
164 | $this.removeClass('ss-gridfield-editable-row--toggle_start');
165 |
166 | if ($parent.hasClass('ss-gridfield-editable-row--loading')) {
167 | return false;
168 | }
169 |
170 | if (link && !$this.hasClass('ss-gridfield-editable-row--toggle_loaded')) {
171 | $parent.addClass('ss-gridfield-editable-row--loading');
172 |
173 | $.ajax({
174 | url: link,
175 | dataType: 'html',
176 | success: function (data) {
177 | var $data = $(data);
178 | $this.addClass('ss-gridfield-editable-row--toggle_loaded ss-gridfield-editable-row--toggle_open');
179 | $parent.addClass('ss-gridfield-editable-row--reference')
180 | .removeClass('ss-gridfield-editable-row--loading');
181 | $parent.after($data);
182 |
183 | $data.find('.ss-gridfield-editable-row--toggle_start').click();
184 |
185 | if (noFocus !== false) {
186 | $data.find("input:first").focus();
187 | }
188 |
189 | if (typeof callback === 'function') {
190 | callback($data, $this, $parent);
191 | }
192 | },
193 | error: function (e) {
194 | alert(ss.i18n._t('GRIDFIELD.ERRORINTRANSACTION'));
195 | $parent.removeClass('ss-gridfield-editable-row--loading');
196 | }
197 | });
198 | }
199 | else if (link) {
200 | var $editable = $parent.next();
201 |
202 | if ($editable.hasClass('ss-gridfield-editable-row--row')
203 | && $editable.data('id') == $parent.data('id')
204 | && $editable.data('class') == $parent.data('class')) {
205 | $this.toggleClass('ss-gridfield-editable-row--toggle_open');
206 |
207 | if ($this.hasClass('ss-gridfield-editable-row--toggle_open')) {
208 | $editable.removeClass('ss-gridfield-editable-row--row_hide')
209 | .find("input:first").focus();
210 | }
211 | else {
212 | $editable.addClass('ss-gridfield-editable-row--row_hide');
213 | }
214 | }
215 | }
216 |
217 | return false;
218 | }
219 | });
220 |
221 | });
222 | })(jQuery);
--------------------------------------------------------------------------------
/code/forms/EditableBlockRow.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 |
11 | use RequestHandler as RequestHandler;
12 | use GridField_HTMLProvider as GridField_HTMLProvider;
13 | use GridField_SaveHandler as GridField_SaveHandler;
14 | use GridField_URLHandler as GridField_URLHandler;
15 | use GridField_ColumnProvider as GridField_ColumnProvider;
16 | use Validator as Validator;
17 | use FieldList as FieldList;
18 | use Session as Session;
19 |
20 | class EditableBlockRow extends RequestHandler implements GridField_HTMLProvider, GridField_SaveHandler, GridField_URLHandler, GridField_ColumnProvider
21 | {
22 | public $column = '_OpenRowForEditing';
23 | public $urlSegment = 'editableRow';
24 | public $setWorkingParentOnRecordTo = 'Parent';
25 | public $disableToggleStateSave = false;
26 | public $cacheToggleStateSave = false;
27 | public $openNewTogglesOnCreate = true;
28 |
29 | protected $permissionCallback;
30 |
31 | protected $itemEditFormCallback;
32 |
33 | protected $fields;
34 |
35 | protected $template;
36 |
37 | protected $validator;
38 |
39 | private $workingGrid;
40 |
41 | private static $allowed_actions = [
42 | 'loadItem',
43 | 'handleForm',
44 | ];
45 |
46 | /**
47 | * @param FieldList|callable|array $fields the fields to display in inline form
48 | */
49 | public function __construct($fields = null)
50 | {
51 | $this->fields = $fields;
52 | parent::__construct();
53 | }
54 |
55 | /**
56 | * Gets the fields for this class
57 | *
58 | * @return FieldList|callable|array
59 | */
60 | public function getFields()
61 | {
62 | return $this->fields;
63 | }
64 |
65 | /**
66 | * Sets the fields that will be displayed in this component
67 | *
68 | * @param FieldList|callable|array $fields
69 | * @return static $this
70 | */
71 | public function setFields($fields)
72 | {
73 | $this->fields = $fields;
74 |
75 | return $this;
76 | }
77 |
78 | /**
79 | * Gets the validator
80 | *
81 | * @return Validator|callable|array
82 | */
83 | public function getValidator()
84 | {
85 | return $this->validator;
86 | }
87 |
88 | /**
89 | * Sets the validator that will be displayed in this component
90 | *
91 | * @param \Validator|Callable|array $validator
92 | * @return static $this
93 | */
94 | public function setValidator($validator)
95 | {
96 | $this->validator = $validator;
97 |
98 | return $this;
99 | }
100 |
101 | /**
102 | * Get the permission callback for this column
103 | *
104 | * @return callable
105 | */
106 | public function getPermissionCallback()
107 | {
108 | return $this->permissionCallback;
109 | }
110 |
111 | /**
112 | * Sets the permission callback for this column
113 | *
114 | * @param callable $permissionCallback
115 | * @return static $this
116 | */
117 | public function setPermissionCallback($permissionCallback)
118 | {
119 | $this->permissionCallback = $permissionCallback;
120 |
121 | return $this;
122 | }
123 |
124 | /**
125 | * Get the callback for changes on the edit form after constructing it
126 | *
127 | * @return callable
128 | */
129 | public function getItemEditFormCallback()
130 | {
131 | return $this->itemEditFormCallback;
132 | }
133 |
134 | /**
135 | * Make changes on the edit form after constructing it.
136 | *
137 | * @param callable $itemEditFormCallback
138 | * @return static $this
139 | */
140 | public function setItemEditFormCallback($itemEditFormCallback)
141 | {
142 | $this->itemEditFormCallback = $itemEditFormCallback;
143 |
144 | return $this;
145 | }
146 |
147 | public function getURLHandlers($gridField)
148 | {
149 | return [
150 | $this->urlSegment . '/load/$ID' => 'loadItem',
151 | $this->urlSegment . '/form/$ID' => 'handleForm',
152 | ];
153 | }
154 |
155 | /**
156 | * Modify the list of columns displayed in the table.
157 | *
158 | * @see {@link GridFieldDataColumns->getDisplayFields()}
159 | * @see {@link GridFieldDataColumns}.
160 | *
161 | * @param \GridField $gridField
162 | * @param array - List reference of all column names.
163 | */
164 | public function augmentColumns($gridField, &$columns)
165 | {
166 | if (!in_array($this->column, $columns)) {
167 | array_unshift($columns, $this->column);
168 | }
169 | }
170 |
171 | /**
172 | * Names of all columns which are affected by this component.
173 | *
174 | * @param \GridField $gridField
175 | *
176 | * @return array
177 | */
178 | public function getColumnsHandled($gridField)
179 | {
180 | return [$this->column];
181 | }
182 |
183 | /**
184 | * HTML for the column, content of the