Silverstripe Elemental trouble after creating related dataobjects - silverstripe-4

Facts: SS 4.0.1, dnadesign/silverstripe-elemental 2.x-dev, php 7.1 and Zauberfisches Vagrant box Jessy Version 3
I made an Elemental Element that is a holder for single elements:
namespace R12page\Elements;
use DNADesign\Elemental\Models\BaseElement;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
use SilverStripe\Versioned\Versioned;
use UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows;
class ImageRasterElement extends BaseElement
{
private static $singular_name = 'Image Raster Element';
private static $plural_name = 'Images Raster Elements';
private static $description = 'Generates an Image Raster for multiple single Elements';
private static $table_name = 'ImageRasterElement';
private static $has_many = [
'SingleElements' => SingleElement::class
];
private static $extensions = [
Versioned::class . '.stagedversioned',
];
public function getType()
{
return 'ImageRasterElement';
}
public function getCMSFields()
{
$fields = parent::getCMSFields();
$singleElements = $this->SingleElements();
$singleElementGridConfig = GridFieldConfig_RecordEditor::create();
$singleElementGridConfig->addComponent(new GridFieldSortableRows('SortOrder'));
$singleElementGrid = GridField::create('SingleElements', 'Single Elements of this Page', $singleElements, $singleElementGridConfig);
$fields->addFieldsToTab('Root.Main', $singleElementGrid);
return $fields;
}
}
This Element has_many single Elements: they look like:
namespace R12page\Elements;
use R12page\Model\News;
use R12page\Model\People;
use R12page\Model\References;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
class SingleElement extends DataObject
{
private static $table_name = 'SingleElement';
private static $db = [
'Reference' => 'Boolean',
'People' => 'Boolean',
'News' => 'Boolean',
'SortOrder' => 'Int'
];
private static $has_one = [
'News' => News::class,
'People' => People::class,
'Reference' => References::class,
'ImageRasterElements' => ImageRasterElement::class
];
private static $summary_fields = [
'News.Headline' => 'News',
'People.Name' => 'People',
'Reference.Headline' => 'Reference'
];
private static $extensions = [
Versioned::class . '.stagedversioned',
];
}
Each Single Element has an has_one to a Data Object. The look like:
namespace R12page\Model;
use R12page\Elements\SingleElement;
use SilverStripe\ORM\DataObject;
use SilverStripe\Versioned\Versioned;
class News extends DataObject
{
private static $table_name = 'News';
private static $db = [
'Headline' => 'Varchar',
'SubHeadline' => 'Varchar',
'Date' => 'Date',
'DatePublished' => 'Date',
'Content' => 'HTMLText',
'IsActiv' => 'Boolean',
'DisplayOnHomePage' => 'Boolean'
];
private static $has_one = [
'Mobile' => Size::class,
'Screen' => Size::Class
];
private static $has_many = [
'SingleElements' => SingleElement::class
];
private static $owns = [
'SingleElements'
];
private static $summary_fields = [
'Headline' => 'Headline',
'Mobile.Title' => 'Mobile',
'Screen.Title' => 'Screen'
];
private static $extensions = [
Versioned::class . '.stagedversioned',
];
}
So far so good. This is what it looks like in the admin Area.
The strange thing is if I add a single Element an try to create it i get this:
When i refresh the page i can see the content of the page and i can save it with out any problems. When if use php_debug everything looks good. I also don't have and error messages in the console which i think, are related to the problem i am having . I just get those to warnings:
But i think the warnings should not be the problem. To be precise i get an error because a font is not loading.
Please help me debug this. I tried the hole day to get this to work. I can not identify the problem i am having.
This is the response i am getting back:
To me it looks okay.
This is the response from the browser networktap:
A view things i can confirm:
Elemental works on a clean install.
If i switch to live mode nothing changes.
If i try to use the content element that ships with elemental it has the same behavior.

Related

Customising filter for ModelAdmin to support date range in SilverStripe

I am developing a SilverStripe project. I am now struggling with customizing the filter/ search for the ModelAdmin entities, https://silverstripe.org/learn/lessons/v4/introduction-to-modeladmin-1. I am trying to add a date range filter as follows.
As you can see there are from and to fields. I have a class called Property and I am trying to customize the search/ filter for the CMS as follow to support the date range filtering.
class Property extends DataObject
{
public function searchableFields()
{
return [
//other fields go here
'Created' => [
'filter' => 'GreaterThanOrEqualFilter',
'title' => 'From',
'field' => DateField::class
],
'Created' => [
'filter' => 'To',
'title' => 'Decision date until',
'field' => DateField::class
],
];
}
}
Only one field is added to the pop up because the array key is overridden. How can I configure it to have the two date fields to specify the date range for the search form?
It might not be relevant now but I bumped to this issue today and I tried your code, you are right, only one field is created I think because you are using single DateField::class. I tried to look for a module that creates a Date Range field and I only can find this one but it looks like it's a project specific.
In my case I have 2 date fields (created and ended), using your code I can get good results by tweaking it to something like this:
public function searchableFields()
{
return [
//other fields go here
'StartDate' => [
'filter' => 'GreaterThanOrEqualFilter',
'title' => 'From',
'field' => DateField::class
],
'EndDate' => [
'filter' => 'LessThanOrEqualFilter',
'title' => 'To',
'field' => DateField::class
],
];
}
Hope it helps someone.
Using this example DataObject create a custom update function updateAdminSearchFields...
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)'));
}
}
Then create an extension that can link that to a ModelAdmin...
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
Lastly on the ModelAdmin apply these filters...
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;
}
}
This example is working on latest stable version 4.7.2

How to modify Product in Silvershop (adding custom fields to $db)

I'm currently developing a shop using SilverShop. I want to add some specific fields to my products, such as what fabric my clothes are made of and an image. I know that we should not make these changes in the core SilverShop source code.
Should I extend the Product class in a new file such as app/src/ProductPage.php?
class Product extends Page implements Buyable
{
private static $db = [
'InternalItemID' => 'Varchar(30)', //ie SKU, ProductID etc (internal / existing recognition of product)
'Model' => 'Varchar(30)',
'BasePrice' => 'Currency(19,4)', // Base retail price the item is marked at.
//physical properties
// TODO: Move these to an extension (used in Variations as well)
'Weight' => 'Decimal(12,5)',
'Height' => 'Decimal(12,5)',
'Width' => 'Decimal(12,5)',
'Depth' => 'Decimal(12,5)',
'Featured' => 'Boolean',
'AllowPurchase' => 'Boolean',
'Popularity' => 'Float' //storage for CalculateProductPopularity task
];
...
Use DataExtension
For SilverStripe 4, it will be something like:
ProductExtension.php :
use SilverStripe\ORM\DataExtension;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
class ProductExtension extends DataExtension
{
private static $db = [
'NewField' => 'Varchar(255)'
];
public function updateCMSFields(FieldList $fields)
{
$fields->addFieldsToTab('Root.Main', TextField::create('NewField', 'This is new field'));
}
}
And, add the next lines to mysite.yml
SilverShop\Page\Product:
extensions:
- ProductExtension
dev/build and it's done

Can I have 2 gridfield components in an admin page in Silverstripe 4

Can I have 2 gridfield components in an admin page named differently from the same dataobject - eg
class MainLandingPage_au extends Page
{
private static $has_many = [
'ImagesWithHtml' => ImageWithHtml::class,
'ImagesWithHtml2' => ImageWithHtml::class,
];
// ...
$fields->addFieldToTab('Root.Section1', HtmlEditorField::create('Section1Title','Section 1 Title')->setRows(4));
$fields->addFieldToTab('Root.Section1', GridField::create(
'ImagesWithHtml',
'Images With Html For This Page',
$this->ImagesWithHtml(),
GridFieldConfig_RecordEditor::create()
));
$fields->addFieldToTab('Root.Section2', HtmlEditorField::create('Section2Title','Section 2 Title')->setRows(4));
$fields->addFieldToTab('Root.Section2', GridField::create(
'ImagesWithHtml2',
'Images With Html For Section 2',
$this->ImagesWithHtml2(),
GridFieldConfig_RecordEditor::create()
));
Did you try it? As you have different relations it should work in general. Of course you need the corresponding has_one relationss on the ImageWithHTML class, see https://docs.silverstripe.org/en/4/developer_guides/model/relations/#has-many
So your code should be something like:
class MainLandingPage_au extends Page
{
private static $has_many = [
'ImagesWithHtml' => `\Namespace\Of\ImageWithHtml.Foo`,
'ImagesWithHtml2' => `\Namespace\Of\ImageWithHtml.Bar`
];
and on the other side
class ImageWithHtml extends DataObject
{
private static $has_one = [
'Foo' => MainLandingPage_au::class,
'Bar' => MainLandingPage_au::class
];
Yes can use 2 gridfields this worked for the proper individual relations
class MainLandingPage_au extends Page
{
private static $has_many = [
'ImagesWithHtml' => ImageWithHtml::class . '.AuMainLandingPage',
'ImagesWithHtml2' => ImageWithHtml::class . '.AuMainLandingPage2'
];
// ...
// Gridfields as in posted question
// ... etc
Other side
class ImageWithHtml extends DataObject
{
private static $has_one = [
'AuMainLandingPage' => AuMainLandingPage::class . '.ImagesWithHtml',
'AuMainLandingPage2' => AuMainLandingPage::class . '.ImagesWithHtml2'
];

SilverStripe and Display Logic fails with certain field types

I'm working on a SilverStripe 3.1.8 site. I have a DataExtension that defines some fields, and I'm trying to use Display Logic to modify the behavior of the CMS:
private static $db = array(
'Enabled' => 'Boolean',
'Title' => 'Text'
);
private static $has_one = array(
'Link' => 'SiteTree',
'Image' => 'Image'
);
/* this is a DataExtension */
public function updateCMSFields(FieldList $fields) {
$fields->addFieldsToTab('Root.Other', array(
CheckboxField::create('Enabled'),
TextField::create('Title', "Title")->displayIf('Enabled')->isChecked()->end(),
TreeDropdownField::create("LinkID", "Linked page", 'SiteTree')->displayIf('Enabled')->isChecked()->end(),
UploadField::create('Image', "Image")->displayIf('Enabled')->isChecked()->end()
));
}
When I check or uncheck the "Enabled" checkbox, the other three fields should appear or disappear correspondingly. Unfortunately, only the TextField does, the TreeDropdownField and UploadField are always shown.
Any ideas why Display Logic fails with these two field types, and how to solve it?
Thank you!
It appears that UploadField and DropdownField fall under the category of Dealing with non-standard forms and you need to wrap them in a DisplayLogicWrapper.
See the last 2 lines of the addFieldsToTab input array.
private static $db = array(
'Enabled' => 'Boolean',
'Title' => 'Text'
);
private static $has_one = array(
'Link' => 'SiteTree',
'Image' => 'Image'
);
/* this is a DataExtension */
public function updateCMSFields(FieldList $fields) {
$fields->addFieldsToTab('Root.Other', array(
CheckboxField::create('Enabled'),
TextField::create('Title', "Title")->displayIf('Enabled')->isChecked()->end(),
DisplayLogicWrapper::create(TreeDropdownField::create("LinkID", "Linked page", 'SiteTree'))->displayIf('Enabled')->isChecked()->end(),
DisplayLogicWrapper::create(UploadField::create('Image', "Image"))->displayIf('Enabled')->isChecked()->end()
));
}

display titles from many_many relation in GridField - Silverstripe

How can I display the titles from a many_many relation in a GridField Summary?
I tried it with RelationName.Title but the result was only an empty field
there should be a couple solutions:
defining $summary_fields on the DataObject that is linked:
private static $summary_fields = array(
'YourFieldName',
'AnotherField'
);
or with the GridFieldConfig on the Page/DataObject that defines the relation:
$config->getComponentByType('GridFieldDataColumns')->setDisplayFields(array(
'FieldName' => 'GridFieldColumnName',
'AnotherFieldName' => 'AnotherGridFieldColumnName',
));
$config being your GridFieldConfig instance used by GridField.
EDIT
for more advanced formatting/control over the data included in the GridField, you can use setFieldFormatting:
$config->getComponentByType('GridFieldDataColumns')->setDisplayFields(array(
'TeamLink' => 'Edit teams'
));
$config->getComponentByType('GridFieldDataColumns')->setFieldFormatting(array(
'TeamLink' => function($value, $item)
{
// $item here would be a TeamMember instance
// since the GridField displays TeamMembers
$links = 'No teams';
$teamModelAdminClass = 'TeamModelAdmin'; //change to your classname
$teams = $item->Teams(); // get the teams
if ( $teams->count() > 0 )
{
$links = '';
$teamClass = $teams->dataClass;
$teamAdminURL = Config::inst()->get($teamModelAdminClass, 'url_segment');
$teamEditAdminURL = 'admin/'.$teamAdminURL.'/'.$teamClass.'/EditForm/field/'.$teamClass.'/item/';
foreach($teams as $team)
{
$links .= 'Edit '.$team->Title.'<br/>';
}
}
return $links;
}
));
Here setFieldFormatting will output edit links to all teams that a TeamMember is part of in a TeamLink column defined by setDisplayFields (might not be the best example, but hope you get the idea, although not tested).
colymba's answer already said most of it, but in addition you can also specify a method in $summary_fields. This allows you to display image thumbnails in a GridField or as you need it, piece together your own string from the titles of a many_many relation.
class TeamMember extends DataObject {
private static $db = array(
'Title' => 'Text',
'Birthday' => 'Date',
);
private static $has_one = array(
'Photo' => 'Image'
);
private static $has_many = array(
'Teams' => 'Team'
);
private static $summary_fields = array(
'PhotoThumbnail',
'Title',
'Birthday',
'TeamsAsString',
);
public function getPhotoThumbnail() {
// display a thumbnail of the Image from the has_one relation
return $this->Photo() ? $this->Photo()->CroppedImage(50,50) : '';
}
public function getTeamsAsString() {
if (!$this->Teams()) {
return 'not in any Team';
}
return implode(', ', $this->Teams()->Column('Title'));
// or if one field is not enough for you, you can use a foreach loop:
// $teamsArray= array();
// foreach ($this->Teams() as $team) {
// $teamsArray[] = "{$team->ID} {$team->Title}";
// }
// return implode(', ', $teamsArray);
}
}
alternative you can, as colymba pointed out, also use setDisplayFields to use different fields on different grids

Resources