Customize ModelAdmin listing in SilverStripe - 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'
);
}

Related

Unable to customize filter/ search form of ModelAdmin in SilverStripe 4.4.4

I am working on a SilverStripe project. Basically, I updated my project to SilverStripe version 4.4.4. After the upgrade, I found out that the search/ filter forms of the ModelAdmin were changed as in the screenshot below.
What I am trying to do now is that I am trying to customize the fields of the search/ filter form of the ModelAdmin following this lesson. https://www.silverstripe.org/learn/lessons/v4/introduction-to-modeladmin-1.
I have a data object class that is linked to a model admin class. Following is the dummy code of the model admin class.
class EnquirySubmission extends DataObject
{
private static $db = [
//some hidden fields are here
];
private static $has_one = [
'Member' => Member::class
];
private static $summary_fields = [
'Member.Name' => 'Member',
//some hidden fields are here
];
//some hidden code goes here
public function searchableFields()
{
return [
'Member.Name' => [
'filter' => 'PartialMatchFilter',
'title' => 'Member',
'field' => \SilverStripe\Forms\DropdownField::create('Member.Name')
->setSource(
Member::get()->map('ID','Email')
)->setEmptyString('-- Member --')
],
];
}
}
As you can see in the code, I am customizing the filter/ search form by overriding the searchableFields method. But it does not work in the upgraded version of SilverStripe. What am I missing and how can I fix it?
Silverstripe and ModelAdmin are ace, but it is confusing why this date range search issue has required a tweak in every version so far. This is a complete example that I've just got working on 4.7.2 (latest stable at time of post)...
app/src/Test/MyDataObject.php
namespace MyVendor\MyNamespace;
use SilverStripe\Forms\DateField;
use SilverStripe\ORM\DataObject;
class MyDataObject extends DataObject {
private static $db = [
'Title' => 'Varchar',
'MyDateTimeField' => 'DBDatetime'
];
private static $summary_fields = ['Title','MyDateTimeField'];
public function updateAdminSearchFields($fields) {
$fields->removeByName('MyDateTimeField');//needed as added in summary field
$fields->push(DateField::create('MyDateTimeField:GreaterThanOrEqual', 'MyDateTimeField (Start)'));
$fields->push(DateField::create('MyDateTimeField:LessThanOrEqual', 'MyDateTimeField (End)'));
}
}
app/src/Test/MyAdmin.php
namespace MyVendor\MyNamespace;
use SilverStripe\Admin\ModelAdmin;
class MyAdmin extends ModelAdmin {
private static $menu_title = 'MyAdmin';
private static $url_segment = 'myadmin';
private static $managed_models = [MyDataObject::class];
public function getList() {
$list = parent::getList();
if ($params = $this->getRequest()->requestVar('filter'))
if ($filters = $params[$this->sanitiseClassName($this->modelClass)])
return $list->filter($filters);
return $list;
}
}
app/src/Test/MyAdminExtension.php
namespace MyVendor\MyNamespace;
use SilverStripe\ORM\DataExtension;
class MyAdminExtension extends DataExtension {
public function updateSearchContext($context) {
$class = $context->getQuery([])->dataClass();
if (method_exists($class, 'updateAdminSearchFields'))
(new $class)->updateAdminSearchFields($context->getFields());
return $context;
}
}
app/_config/mysite.yml
MyVendor\MyNamespace\MyAdmin:
extensions:
- MyVendor\MyNamespace\MyAdminExtension

Silverstripe 3 - GridFieldExtensions MultiClass Adding

I'm trying to use https://github.com/silverstripe-australia/silverstripe-gridfieldextensions/ to create a gridfield where I can add different types of dataobjects.
Sadly I can't figure out how to write the correct code for that on my class where I want the gridfield.
Could someone please point me in the right direction?
UPDATE:
Based on your answers, I now have the following structure
class ModularPage extends Page {
private static $has_many = array(
'Sections' => 'MP_Section',
'Galleries' => 'MP_Gallery',
'Paragraphs' => 'MP_Paragraph'
);
public function getCMSFields() {
...
$fields->addFieldToTab('Root.Main', $mutli_grid = GridField::create('Sections', 'Sektionen', $this->Sections(), MultiClassGrid::create(15)));
...
}
}
class MP_Section extends DataObject {
private static $has_one = array(
'Section' => 'MP_Section',
'ModularPage' => 'ModularPage'
);
}
class MP_Gallery extends MP_Section {
private static $has_one = array(
'Section' => 'MP_Section',
'ModularPage' => 'ModularPage'
);
}
So far, so good? Is this right until now?
Cause If I want to add for example a gallery, I receive the following error
[User Error] Couldn't run query: SELECT DISTINCT "MP_Section"."ID", "MP_Section"."SortID" FROM "MP_Section" LEFT JOIN "MP_Gallery" ON "MP_Gallery"."ID" = "MP_Section"."ID" LEFT JOIN "MP_Paragraph" ON "MP_Paragraph"."ID" = "MP_Section"."ID" WHERE ("ModularPageID" = '13') ORDER BY "MP_Section"."SortID" ASC LIMIT 9223372036854775807 Column 'ModularPageID' in where clause is ambiguous
Here is how I usually setup my GridField:
$c = GridFieldConfig_RelationEditor::create();
$c->removeComponentsByType('GridFieldAddNewButton')
->addComponent(new GridFieldAddNewMultiClass())
;
$c->getComponentByType('GridFieldAddNewMultiClass')
->setClasses(array(
'SectionThemesBlock' => SectionThemesBlock::get_section_type(),
'SectionFeaturedCourse' => SectionFeaturedCourse::get_section_type(),
'SectionCallForAction' => SectionCallForAction::get_section_type(),
'SectionContactSheet' => SectionContactSheet::get_section_type()
//....
));
$f = GridField::create('Sections', "Sections", $this->Sections(), $c);
$fields->addFieldToTab("Root.Sections", $f);
Based on the GridFieldConfig_RelationEditor, just remove GridFieldAddNewButton then add GridFieldAddNewMultiClass. Then configure the component to know which classes to have available in the dropdown to create. All those SectionThemesBlock, SectionFeaturedCourse etc extend a common Section dataObject as base. The get_section_type() function is a custom static function on the Section dataobject to get a nice looking name in the dropdown and not have to type it manually all the time....
The basics of the Section dataobject looks like so:
class Section extends DataObject {
public static function get_section_type()
{
return trim(preg_replace('/([A-Z])/', ' $1', str_ireplace('Section', '', get_called_class())));
}
//...
}
And the page where that gridField goes and that has the relation defined on:
class Page extends SiteTree {
//...
private static $has_many = array(
'Slides' => 'Slide'
);
//...
}
Something like this should work
$config = new GridFieldConfig_RecordEditor();
$config->addComponent(new GridFieldAddNewMultiClass());
...
$grid = GridField::create('Grid', 'Grid', $this->GalleryItems(), $config);
You need three DataObjects:
GalleryItem extends DataObject{}
FooGalleryItem extends GalleryItem{}
BarGalleryItem extends GalleryItem{}

Silverstripe 3 - extend sitetree and layout

I'm a newbie in SilverStripe. I need to create a new page by extending a new class sitetree. My question is how to retrieve in the template something like $Layou which is used in the classic page.php. For example, in my template folder I would like to have a folder like "Layout" organized for new pages created from this.
this is the controller:
class WhitePage extends SiteTree {
private static $db = array(
);
private static $has_one = array(
);
}
class WhitePage_Controller extends ContentController {
private static $allowed_actions = array(
);
public function init() {
parent::init();
}
function index() {
return $this->renderWith("WhitePage");
}
}
i would like in the template directory create a folder name "whitepage" within the ".ss" file, and in the template use something like $whitepage instate of $Layout ...
How to do this?
thk, a lot
Francesco
You can use the master WhitePage.ss template with several page types by extending your WhitePage class. Then you can use the $Layout as normal to call your custom layout template.
WhitePage
class WhitePage extends SiteTree {
}
class WhitePage_Controller extends ContentController {
private static $allowed_actions = array(
);
public function init() {
parent::init();
}
}
CustomWhitePage
class CustomWhitePage extends WhitePage {
}
class CustomWhitePage_Controller extends WhitePage_Controller {
private static $allowed_actions = array(
);
public function init() {
parent::init();
}
}
In your themes/mytheme folder or mysite folder create your templates like so:
templates/Page.ss
templates/WhitePage.ss
templates/Layout/Page.ss
templates/Layout/WhitePage.ss
templates/Layout/CustomWhitePage.ss
Your Layout/WhitePage.ss and Layout/CustomWhitePage.ss will use the templates/WhitePage.ss parent template while any page that extends Page will use templates/Page.ss.
Make sure you call ?flush=all for your templates to be loaded the first time through.

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

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 )

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