Add new Page using GridField - creates the child in root folder - silverstripe

I would like to use GridField to view and create new child pages. Parent is DocumentHolder, child is Document. Both extend SiteTree. When I click to "Add Document" (button generated by grid), fill in the fields and confirm the form, the parent page is ignored and the page is created in root. It works well when I use DataObject. The code looks like this:
class DocumentHolder extends SiteTree
{
private static $allowed_children = array(
'Document'
);
private static $default_child = "Document";
public function getCMSFields()
{
$fields = parent::getCMSFields();
$gridField = new GridField('Documents', 'Documents', SiteTree::get('Document')->filter('ParentID', $this->ID), GridFieldConfig_RecordEditor::create());
$fields->addFieldToTab("Root.Uploads", $gridField);
return $fields;
}
}
class Document extends SiteTree
{
private static $db = array(
);
private static $has_one = array(
);
}
Thanks for help.

Since SiteTree already has a relationship to its Children pages set up, you may as well use it! Since allowed_children will only ever be documents, try this instead:
$gridField = new GridField('Documents', 'Documents', $this->Children(), GridFieldConfig_RecordEditor::create());

I ran into this problem earlier working on my holderpage module. You need to set the ParentID by default. Here's two strategies;
You can use populateDefaults on the child class. E.g.
class Document extends SiteTree
{
private static $default_parent = 'DocumentHolder';
private static $can_be_root = false;
public function populateDefaults(){
parent::populateDefaults();
$this->ParentID = DataObject::get_one(self::$default_parent)->ID;
}
...
Or you can manipulate the record in the gridfield with a custom GridFieldDetailForm implementation or via the updateItemEditForm callback.
<?php
class MyGridFieldDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
{
public function ItemEditForm()
{
$form = parent::ItemEditForm();
if (! $this->record->exists() && $this->record->is_a('SiteTree')) {
$parent_page = $this->getController()->currentPage();
if ($parent_page && $parent_page->exists()) {
$this->record->ParentID = $parent_page->ID;
// update URLSegment #TODO perhaps more efficiently?
$field = $this->record->getCMSFields()->dataFieldByName('URLSegment');
$form->Fields()->replaceField('URLSegment', $field);
}
}
return $form;
}
}
This is more complicated although it allowed me to create an effortless module / addon ( https://github.com/briceburg/silverstripe-holderpage )

Related

Remove Add and Delete buttons from DataObject ModelAdmin

I have custom data object with predefined entries. I don't want user to delete or add any new entries from GridField's edit form. Is there a way to remove those two buttons form ModelAdmins GridField edit form?
Using: Silverstripe 3.6
To remove actions from a GridField "globally", eg. for all records managed by the GridField, it's best to modify the GridFieldConfig instance.
In a ModelAdmin context, this is possible by overriding getEditForm:
public function getEditForm($id = null, $fields = null)
{
$form = parent::getEditForm($id, $fields);
// make sure to check if the modelClass matches the object you want to edit
// otherwise, the config will get applied to all models managed
// by this ModelAdmin instance
if ($this->modelClass === Translation::class) {
$fieldName = $this->sanitiseClassName($this->modelClass);
/** #var GridField $grid */
if ($grid = $form->Fields()->dataFieldByName($fieldName)) {
$grid->getConfig()->removeComponentsByType([
GridFieldDeleteAction::class,
GridFieldAddNewButton::class
]);
}
}
return $form;
}
However, the user might still be able to delete a record in the detail-view. But since both, GridField and detail-view respect DataObject permissions, you should make use of them… this also prevents that a user can delete the object via other means.
A simplistic solution would be (these methods should be implemented in your DataObject):
public function canDelete($member = null)
{
return Permission::check('ADMIN');
}
public function canCreate($member = null)
{
return Permission::check('ADMIN');
}
public function canView($member = null)
{
return true;
}
public function canEdit($member = null)
{
return Permission::check('CMS_ACCESS_TranslationAdmin');
}
That way, only administrators can create/delete these objects. They can be viewed by all users and edited by users that have access to your ModelAdmin section (here named "TranslationAdmin").
Ok, i got it my self. If you want to remove Add and Delete buttons from managed model's ModelAdmin you need to add this code
class Translation extends DataObject {
// ...
public function canDelete($member = null) {
return false;
}
}
class TranslationAdmin extends ModelAdmin {
public static $managed_models = ['Translation'];
static $url_segment = 'translations';
static $menu_title = 'Translations';
public function getEditForm($id = null, $fields = null) {
$form = parent::getEditForm($id, $fields);
$form
->Fields()
->fieldByName($this->sanitiseClassName($this->modelClass))
->getConfig()
->removeComponentsByType('GridFieldDeleteAction')
->removeComponentsByType('GridFieldAddNewButton');
return $form;
}
}
Hope this helps for someone in future.

SilverStripe remove CSV Export button

I'm trying to remove the Export to CSV button in the top of a GridField in ModelAdmin.
I can't seem to find the class that creates the button (GridFieldExportButton right?). I'm guessing there is a function that populates the GridField with buttons / "actions" which I'm not familiar with.
To remove the scaffolded GridField for relationships...
class MyDataObject extends DataObject {
...
private static $has_many= array(
'OtherDataObjects' => 'OtherDataObject'
);
...
function getCMSFields() {
$fields = parent::getCMSFields();
if($grid = $fields->dataFieldByName('OtherDataObjects'))
$grid->getConfig()
->removeComponentsByType('SilverStripe\Forms\GridField\GridFieldExportButton');
return $fields;
}
...
}
If you are making the GridField then just add this when you create the field...
$gridField->getConfig()->removeComponentsByType('SilverStripe\Forms\GridField\GridFieldExportButton');
If you are looking for a gridfield that isn't within a data object edit form and is actually...
class MyAdmin extends ModelAdmin {
...
function getEditForm($id = null, $fields = null) {
$form = parent::getEditForm($id, $fields);
if($this->modelClass == 'MyDataObjectName') {
$form->Fields()
->fieldByName($this->sanitiseClassName($this->modelClass))
->getConfig()
->removeComponentsByType('SilverStripe\Forms\GridField\GridFieldExportButton');
}
return $form;
}
...
}
Setting model_importers to empty will do the reverse and remove the import ...
class MyAdmin extends ModelAdmin {
...
static $model_importers = array();
...
}

Customize ModelAdmin listing in SilverStripe

Is it possible to change or add custom summary_fields into a listing from a ModelAdmin extends? Actually I'm able to filter a custom field named Type but I don't know how to customize the summary_fields. This is my actual code:
class Profiles3ModelAdmin extends ModelAdmin {
public static $menu_icon = 'mysite/images/peoples.png';
public static $managed_models = array('Member');
public static $url_segment = 'membres';
public static $menu_title = 'Membres';
public function getList() {
$group = Group::get()->filter('Code', array('Membres'))->first();
$list = $group->Members()->filter('Type', 1 );
return $list;
}
}
Your current query should be able to use $list = Member::get()->filter(array('Groups.Code' => 'Membres', 'Type' => 1)); if I recall correctly. Just gets it down to one line.
Usually to add to the summary you would add it to your class's model. So in this case on Member you would apply a DataExtension that had:
<?php
class MyMemberDataExtension extends DataExtension{
private static $summary_fields = array(
'Type'
);
}

Can't add VirtualPage as allowed children

I have created a holder page KochiHolder as below and performed a database rebuild successfully. I have set the $allowed_children of KochiHolder to only accept KochiPage or VirtualPage.
In the CMS I cannot create a VirtualPage under a KochiHolder page.
Why is $allowed_children not working correctly? How do I allow VirtualPage to be created as a sub page of KochiHolder?
KochiHolder
class KochiHolder extends Page {
private static $db = array(
);
private static $has_one = array(
'Photo' => 'Image'
);
private static $can_be_root = false;
private static $allowed_children = array('KochiPage', 'VirtualPage');
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab("Root.Images", new UploadField('Photo'));
return $fields;
}
}
After a bit of debugging of CMSPageAddController (more specifically, the doAdd action) that Silverstripe uses for the "Add Page" screen in the CMS, I believe I found the root cause of the problem.
So the following error seems to be occurring: Page type "Page" not allowed as child of this parent page (well, that is the error I was getting in my test)
In the SiteTree class, it has a validate function which checks what the allowed children are. One of the checks for this is unrolling VirtualPage to the page it is a virtual of. (There is probably a little more to it than that - I need to do more investigating)
Good news is that you can override this functionality however it isn't great. Basically, because your class extends Page which extends SiteTree, if you specify your own validate function on Page (as VirtualPage extends Page too) you would be able to override the functionality.
In my tests, I used the following:
public function validate()
{
$result = parent::validate();
if ($this instanceof VirtualPage)
{
$newResult = new ValidationResult();
$items = $result->messageList();
foreach ($items as $key => $value)
{
if ($key != 'ALLOWED_CHILDREN')
{
$newResult->error($value, $key);
}
}
return $newResult;
}
return $result;
}
It isn't perfect and it makes some assumptions (eg. error codes are assumed for all items in the messageList but that isn't always the case). I haven't done heaps of testing with it but it does work for my recreation of your particular scenario.

SilverStripe - limiting the number of many relations a dataobject can have

If I have a $has_many relationship that I want to manage with a GridField in the cms, how would I go about putting a limit on the number of how many relations one object can have? Is this possible?
Can I do this in the model or would it have to be a check I add into the GridField I'm using to add and remove relations?
I'm looking at implementing GridField_SaveHandler to make a custom GridFieldComponent but not sure how I can use this to abort the save if i detect something is wrong.
the following 2 solutions are not the cleanest way to solve this, but the most pragmatic and easiest to implement.
basically, what I suggest to do, is just count the objects and remove the ability to add new records once the count is above a certain number.
if you want to limit the number of records on a single relation/grid (lets say max 5 players per team):
class Player extends Dataobject {
private static $db = array('Title' => 'Varchar');
private static $has_one = array('TeamPage' => 'TeamPage');
}
class TeamPage extends Page {
private static $has_one = array('Players' => 'Player');
public function getCMSFields() {
$fields = parent::getCMSFields();
$config = GridFieldConfig_RecordEditor::create();
if ($this->Players()->count > 5) {
// remove the buttons if we don't want to allow more records to be added/created
$config->removeComponentsByType('GridFieldAddNewButton');
$config->removeComponentsByType('GridFieldAddExistingAutocompleter');
}
$grid = GridField::create('Players', 'Players on this Team', $this->Players(), $config);
$fields->addFieldToTab('Root.Main', $grid);
return $fields;
}
}
if you want to limit the total number of records globally (if we limit this way to 5, this means if 1 Team already has 3 Players, then the 2nd team can only have 2):
class Player extends Dataobject {
private static $db = array('Title' => 'Varchar');
private static $has_one = array('TeamPage' => 'TeamPage');
public function canCreate($member = null) {
if (Player::get()->count() > 5) {
return false;
}
return parent::canCreate($member);
}
}
class TeamPage extends Page {
private static $has_one = array('Players' => 'Player');
public function getCMSFields() {
$fields = parent::getCMSFields();
$config = GridFieldConfig_RecordEditor::create();
$grid = GridField::create('Players', 'Players on this Team', $this->Players(), $config);
$fields->addFieldToTab('Root.Main', $grid);
return $fields;
}
}
I have wrote a quick jQuery plugin to limit the number of items a GridField can have: -
Download the plugin here: - gridfieldlimit.js
https://letscrate.com/f/monkeyben/silverstripe/gridfieldlimit.js
Set up the plugin in the getCMSFields function: -
// Pass GridField configs, each one containing field name and item limit
$vars = array(
"GridFieldLimits" => "[['GRIDFIELD_NAME_1', 3], ['GRIDFIELD_NAME_2', 6]]",
);
// Load the jquery gridfield plugin
Requirements::javascriptTemplate("themes/YOUR_THEME_NAME/javascript/gridfieldlimit.js", $vars);
works for me: make the canCreate method of the DataObject that's managed by your GridField check for existing objects.
of course, this doesn't allow you to implement a customg GridFieldComponent, as you need to modify the DataObject code.

Resources