How to make fields required in CMS - php

I try to make fields required in the CMS:
class Documents extends DataObject {
private static $db = array(
'DocType' => 'Text',
'DocTitle' => 'Text',
'DocNumber' => 'Text'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields = FieldList::create(TabSet::create('Root'));
$fields->addFieldsToTab('Root.Main', array(
DropdownField::create('DocType','Document Type'),
DropdownField::create('DocStatus','Document Status'),
TextField::create('DocNumber','Document Number'),
...
RequiredFields::create(array('DocType','DocTitle','DocNumber'));
));
return $fields;
}
But in my SilverStripe error log I get the following:
"Uncaught Exeption: the method 'getname' doesn't exist on RequiredFields or the method is not public".
How do I make fields required in the SilverStripe CMS?

In the CMS we can declare required fields by declaring a getCMSValidator function and returning RequiredFields:
public function getCMSValidator()
{
return RequiredFields::create(
'DocType',
'DocTitle',
'DocNumber'
);
}

Related

Silverstripe 4 getCMSFields_forPopup and GridField

Picking this up again after many years. Can I not use gridfield within the cms popup component? Here I have Ingredient entity and am wanting to add Ingredients from the db to a Recipe entity. Even a simple one doesn't appear.
Recipe.php
...
private static $db = [
'Title' => 'Varchar',
'Description' => 'Text',
];
private static $has_one = [];
private static $many_many = [
'Ingredients' => Ingredient::class,
];
public function getCMSFields_forPopup()
{
$gridConfig = GridFieldConfig_RelationEditor::create()->addComponents(
new GridFieldDeleteAction('unlinkrelation')
);
$grid = GridField::create(
'Ingredients',
'Ingredients',
$this->Ingredients(),
$gridConfig,
);
$fields = FieldList::create(
TextField::create('Title'),
TextareaField::create('Description'),
$grid
);
// or maybe something like..
// $fields->addFieldToTab('Main', 'Ingredients', 'Ingredients', $grid);
return $fields;
}
getCMSFields_forPopup does not exist in Silverstripe 4 or Silverstripe 3. This was in Silverstripe 2.
Try getCMSFields instead.
public function getCMSFields()
{
$fields = parent::getCMSFields();
$ingredientsFieldConfig = GridFieldConfig_RelationEditor::create();
$ingredientsField = GridField::create(
'Ingredients',
'Ingredients',
$this->Ingredients(),
$ingredientsFieldConfig
);
$fields->addFieldToTab('Root.Main', $ingredientsFieldConfig);
return $fields;
}

Silverstripe: $has_many summary fields issue

I am trying to use a Has_many relation as the summary fields for a DataObject and can't seem to get it working.
Basically:
I have a Form
each form has many submissions/entries
each form has many fields
Each field has many answers.
I'm trying to create a gridfield in the back end admin area of each form which displays the entries for each form.
In the summary fields for the entry, i'd like to display the Date created, and the first 3 fields for that form.
So, for example if we had a form with a name, email, and phone field the summary fields would be as follows:
Date Created
Name
Email
Phone
with the relevent entry data/responses as summary information for the entry.
Here is what I have so far. This is the form:
<?php
class ContactBlock extends Block {
private static $db = array(
// Fields for the form/block go here
);
private static $has_many = array(
'ContactBlockFields' => 'ContactBlockField',
'ContactBlockEntries' => 'ContactBlockEntry'
);
public function getCMSFields() {
// Irrelevant code goes here!
// CONTACT BLOCK ENTRIES
$entriesInfo = new GridFieldDataColumns();
$entriesInfo->setDisplayFields(singleton('ContactBlockEntry')->summaryFields());
$entriesConfig = GridFieldConfig::create();
$entriesConfig->addComponents(
new GridFieldToolbarHeader(),
new GridFieldAddNewButton('toolbar-header-right'),
$entriesInfo,
new GridFieldSortableHeader(),
new GridFieldPaginator(50),
new GridFieldEditButton(),
new GridFieldDeleteAction(),
new GridFieldDetailForm()
);
$entriesGrid = GridField::create('ContactBlockEntries', 'Form Entries', $this->ContactBlockEntries(), $entriesConfig);
$fields->addFieldToTab('Root.FormEntries', $entriesGrid);
return $fields;
}
}
This is the Entry DataObject:
<?php
class ContactBlockEntry extends DataObject {
private static $has_one = array(
'ContactBlock' => 'ContactBlock'
);
private static $has_many = array(
'ContactBlockFieldAnswers' => 'ContactBlockFieldAnswer',
);
private static $many_many = array(
'FormFields' => 'ContactBlockField'
);
static $summary_fields = array(
'Date'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
//=== REMOVE FIELDS ====
$fields->removeFieldFromTab('Root','FormFields');
$fields->removeFieldFromTab('Root','ContactBlockFieldAnswers');
$fields->removeFieldFromTab('Root.Main','ContactBlockID');
//=== REMOVE FIELDS ====
return $fields;
}
public function onBeforeDelete() {
parent::onBeforeDelete();
// Delete answers that are associated with this block.
$data = ContactBlockFieldAnswer::get()
->filter('ContactBlockEntry', $this->ID);
foreach( $data as $d) {
$d->delete();
}
}
public function getDate() {
$date = date('d/m/Y',strtotime($this->Created));
return $date;
}
}
This is the field code:
<?php
class ContactBlockField extends DataObject {
private static $db = array(
'SortOrder' => 'Int',
'FieldName' => 'Varchar',
'FieldType' => 'Varchar',
'DropdownValues' => 'Varchar(255)',
'Label' => 'Varchar',
'Placeholder' => 'Varchar',
'Required' => 'Boolean'
);
private static $has_one = array(
'ContactBlock' => 'ContactBlock',
);
private static $has_many = array(
'ContactBlockFieldAnswer' => 'ContactBlockFieldAnswer',
);
private static $belongs_many_many = array(
'Entries' => 'ContactBlockEntry'
);
static $searchable_fields = array(
'FieldType',
'FieldLabel',
'Required'
);
static $summary_fields = array(
'FieldType' => 'Field Type',
'Label' => 'Field Label',
'Required' => 'Required Field?'
);
public function getCMSFields() {
$fields = parent::getCMSFields();
// Unrelated stuff here
return $fields;
}
}
I can't seem to figure out how to get the column labels, and their relevant data showing on the gridfield. Any help or advice would be much appreciated.
UPDATE 24/3/17:
OK I've got a little further with this. On the ContactBlockEntry DataObject, after implementing the changes suggested by UncleCheese, I have discovered the following:
public function getFirstField() {
return $this->FormFields()->first();
}
public function getSecondField() {
return $this->FormFields()->offsetGet(1);
}
public function getThirdField() {
return $this->FormFields()->offsetGet(2);
}
public function summaryFields() {
return [
'Date' => 'Submitted',
'Answers.First.Value' => $this->getFirstField()->Label,
'Answers.Second.Value' => $this->getSecondField()->Label,
'Answers.Third.Value' => $this->getThirdField()->Label,
];
}
$this->getFirstField()->Label is returning [Notice] Trying to get property of non-object however, when I echo/print this function in getCMSFields() I can see the label value.
Answers.First.Value works. It returns the value/answer submitted in the first field. The problem is, I can't seem to get the second and third values, as I can't figure out the method to retrieve them. I tried offsetGet() and it said the method isn't available.
In this case, you can define a summaryFields() method in lieu of the static array. This will allow you to return a computed value for the headings.
What complicates things is getting the values. The first three fields are not properties of the Entry object, so you'll need to provide getters for the Entry dataobject to compute them on the fly as "virtual" properties. It's a bit clunky, but something like this should work.
public function getFirstField()
{
return $this->FormFields()->first();
}
public function getSecondField()
{
return $this->FormFields()->offsetGet(1);
}
public function getThirdField()
{
return $this->FormFields()->offsetGet(2);
}
public function summaryFields()
{
return [
'Created' => 'Created',
'FirstField.Answer' => $this->getFirstField()->FieldName,
'SecondField.Answer' => $this->getSecondField()->FieldName,
'ThirdField.Answer' => $this->getThirdField()->FieldName,
];
}
I'm not too sure about your data model, though. You have the answers has a has_many on your field object. You would in that case have to create another getter on your Field object for AnswerLabel that somehow concatenated all the answers into a string, or maybe just got the first one. Whatever business logic you choose. Just use FirstField.AnswerLabel etc. in your array, or whatever you choose to call that method. The point being, you need to resolve a plural relationship into a single readable value. How you do that is up to you.

I do not see the save button on my form even though I have set it up in Admin Area Magento

I am new to mangeto framework and i am learning to create a form in admin area section. However, it has been over hours that I could not figure out the error i am receiving:
Recoverable Error: Argument 1 passed to Mage_Adminhtml_Controller_Action::_addContent() must be an instance of Mage_Core_Block_Abstract, boolean given, called in /vagrant/magento/app/code/local/MasteringMagento/Example/controllers/Adminhtml/EventController.php on line 12.
The following is my Edit.php file as well as my Form.php file
Edit.php:
class MasteringMagento_Example_Adminhtml_EventController extends Mage_Adminhtml_Controller_Action{
public function indexAction(){
$this->loadLayout();
$this->_addContent(
$this->getLayout()->createBlock('example/adminhtml_event_edit'));
//go straight to the php file to render the form. otherwise this will not perfomed.
$this->renderLayout();
}
public function saveAction(){
$eventID = $this->getRequest()->getParam('event_id');
$eventModel = Mage::getModel('example/event')->load($eventID);
if($data = $this->getRequest()->getPost()){
try{
$eventModel->addData($data)->save();
Mage::getSingleton('adminhtml/session')->addSuccess(
$this->__('Your event has been saved')
);
}catch(Exception $e){
Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
}
$this->_redirect('*/*/index');
}
}
}
and Form.php file:
class MasteringMagento_Example_Block_Adminhtml_Event_Edit_Form extends Mage_Adminhtml_Block_Widget_Form{
protected function _prepareForm(){
$form = new Varien_Data_Form(array('id'=>'edit_form',
'action'=>$this->getData('action'), 'method'=>'post'));
$fieldset = $form->addFieldset('base_fieldset',
array('legend'=>Mage::helper('example')->__('General Information'),
'class'=>'fieldset-wide'));
$fieldset->addField('name', 'text', array(
'name' => 'name',
'label' => Mage::helper('example')->__('Event Name'),
'title' => Mage::helper('example')->__('Event Name'),
'required' => true
));
$dateFormatIso = Mage::app()->getLocale()->getDateFormat(
Mage_Core_Model_Locale::FORMAT_TYPE_SHORT);
$fieldset->addField('start', 'date', array(
'name' => 'start',
'format' => $dateFormatIso,
'image' => $this->getSkinUrl('images/grid-cal.gif'),
'label' => Mage::helper('example')->__('Start Date'),
'title' => Mage::helper('example')->__('Start Date'),
'required' => true
));
$fieldset->addField('end', 'date', array(
'name' => 'end',
'format' => $dateFormatIso,
'image' => $this->getSkinUrl('images/grid-cal.gif'),
'label' => Mage::helper('example')->__('End Date'),
'title' => Mage::helper('example')->__('End Date'),
'required' => true
));
$form->setUseContainer(true);
$this->setForm($form);
return parent::_prepareForm();
}
}
The error i think is from my Controller. However, if i direct the url link to the form, it will display. But if i direct to its container which is Edit.php, the error above would occurs:
class MasteringMagento_Example_Adminhtml_EventController extends Mage_Adminhtml_Controller_Action{
public function indexAction(){
$this->loadLayout();
$this->_addContent(
$this->getLayout()->createBlock('example/adminhtml_event_edit'));
//go straight to the php file to render the form. otherwise this will not perfomed.
$this->renderLayout();
}}
This is my config.xml. I did include the base class for Magento Blocks:
<blocks>
<example>
<class>MasteringMagento_Example_Block</class>
</example>
</blocks>
Please help me to identify the problem. Thanks
createBlock() is an abstract factory pattern for Blocks inside of Magento. Whenever Magento cannot resolve a factory class from this method, a boolean is returned... which is the case in your example, as the error message says.
Check your class MasteringMagento_Example_Block_Adminhtml_Event_Edit for spelling, casing, or class related errors. Also ensure that your class file is located at app/code/local/MasteringMagento/Example/Block/Adminhtml/Event/Edit.php.
In magento, every admin form block is loaded first by a form container.
Here, you call MasteringMagento_Example_Block_Adminhtml_Event_Edit class :
$this->getLayout()->createBlock('example/adminhtml_event_edit')
This class, located in app/code/local/MasteringMagento/Example/Block/Adminhtml/Post/Edit.php, should look to something like this :
<?php
/**
* MasteringMagento_Example_Block_Adminhtml_Post_Edit
*/
class MasteringMagento_Example_Block_Adminhtml_Post_Edit extends Mage_Adminhtml_Block_Widget_Form_Container
{
public function __construct()
{
// $this->_objectId = 'id';
parent::__construct();
$this->_blockGroup = 'example';
$this->_controller = 'adminhtml_post';
$this->_mode = 'edit';
$modelTitle = $this->_getModelTitle();
$this->_updateButton('save', 'label', $this->_getHelper()->__("Save $modelTitle"));
$this->_addButton('saveandcontinue', array(
'label' => $this->_getHelper()->__('Save and Continue Edit'),
'onclick' => 'saveAndContinueEdit()',
'class' => 'save',
), -100);
$this->_formScripts[] = "
function saveAndContinueEdit(){
editForm.submit($('edit_form').action+'back/edit/');
}
";
}
protected function _getHelper(){
return Mage::helper('example');
}
protected function _getModel(){
return Mage::registry('exemple_youmodel');
}
protected function _getModelTitle(){
return 'Post';
}
public function getHeaderText()
{
$model = $this->_getModel();
$modelTitle = $this->_getModelTitle();
if ($model && $model->getId()) {
return $this->_getHelper()->__("Edit $modelTitle (ID: {$model->getId()})");
}
else {
return $this->_getHelper()->__("New $modelTitle");
}
}
/**
* Get URL for back (reset) button
*
* #return string
*/
public function getBackUrl()
{
return $this->getUrl('*/*/index');
}
public function getDeleteUrl()
{
return $this->getUrl('*/*/delete', array($this->_objectId => $this->getRequest()->getParam($this->_objectId)));
}
}
As you can see, all your buttons are set in the __construct() method.
Hope it helps.

SilverStripe gridField Entries not visible for normal backend user

I have two user groups Administrator and Inhaltsautoren
My LandingPage has a Tab Teaser with a gridField. The normal user can not see the entries and i dont know why?
I cant find something for setting the permissions for Inhaltsautoren. Has someone an idea why there are no entries in the gridField?
Teaser.php
<?php
class Teaser extends DataObject {
private static $db = array (
'Title' => 'Varchar',
'Description' => 'HTMLText'
);
private static $has_one = array (
'Photo' => 'Image',
'Link' => 'Link'
);
private static $many_many = array(
'Tags' => 'Tag'
);
private static $summary_fields = array (
'GridThumbnail' => '',
'Title' => 'Titel',
'Description' => 'Beschreibung'
);
public function getGridThumbnail() {
if($this->Photo()->exists()) {
return $this->Photo()->SetWidth(100);
}
return "(no image)";
}
public function getCMSFields() {
$fields = FieldList::create(
TextField::create('Title'),
$tags = TagField::create('Tags','Tags',Tag::get(),$this->Tags()),
HTMLEditorField::create('Description', 'Beschreibung'),
LinkField::create('LinkID', 'Weiterleitung'),
$uploader = UploadField::create('Photo')
);
$tags->setShouldLazyLoad(true); // tags should be lazy loaded
$tags->setCanCreate(true); // new tag DataObjects can be created
$uploader->setFolderName('teaser');
$uploader->getValidator()->setAllowedExtensions(array('png','jpeg','jpg'));
return $fields;
}
}
and my LadingPage.php
$fields->addFieldToTab('Root.Teaser', $gridField = GridField::create(
'Teasers',
'Landing Page Teaser',
$this->Teasers(),
GridFieldConfig_RecordEditor::create()
));
$gridField->getConfig()->getComponentByType("GridFieldDataColumns")->setFieldCasting(array("Description"=>"HTMLText->BigSummary"));
Use canView() on your dataobject and check inside this function if your user is allowed to see this object or not.
public function canView($member = null) {
return Permission::check('ADMIN', 'any');
}

Magento custom admin module wysiwyg integration

I've created an admin module based off of the tutorial here. I'm attempting to change two of my form inputs to wysiwyg editors based off of information found here. However, whenever I load the edit page I get an error Call to a member function setCanLoadTinyMce() on a non-object. $this->getLayout()->getBlock('head') var_dumps to false.
Namespace/Slides/Block/Adminhtml/Slide/Edit.php looks as follows
class Namespace_Slides_Block_Adminhtml_Slide_Edit
extends Mage_Adminhtml_Block_Widget_Form_Container
{
protected function _prepareLayout()
{
parent::_prepareLayout();
if (Mage::getSingleton('cms/wysiwyg_config')->isEnabled()) {
$this->getLayout()->getBlock('head')->setCanLoadTinyMce(true);
}
}
protected function _construct()
{
//... Construction stuff
}
}
Namespace/Slides/Block/Adminhtml/Slide/Edit/Form.php
class Cfw_Slides_Block_Adminhtml_Slide_Edit_Form
extends Mage_Adminhtml_Block_Widget_Form
{
protected function _prepareForm()
{
//...Do some things first, like create the fieldset..
//Add the editable fields
$this->_addFieldsToFieldset($fieldset, array(
'foreground_image' => array(
'label' => $this->__('Foreground Image'),
'input' => 'image',
'required' => false,
),
'background_image' => array(
'label' => $this->__('Background Image'),
'input' => 'editor',
'required' => true,
'config' => Mage::getSingleton('cms/wysiwyg_config')->getConfig(),
'wysiwyg' => true,
),
'description' => array(
'label' => $this->__('Text Overlay'),
'input' => 'editor',
'required' => false,
'config' => Mage::getSingleton('cms/wysiwyg_config')->getConfig(),
'wysiwyg' => true,
)
));
return $this;
}
protected function _addFieldsToFieldset(
Varien_Data_Form_ElementFieldset $fieldset, $fields)
{
$requestData = new Varien_Object($this->getRequest()->getPost('slideData'));
foreach ($fields as $name => $_data) {
if ($requestValue = $requestData->getData($name)) {
$_data['value'] = $requestValue;
}
//Wrap all fields with slideData group
$_data['name'] = "slideData[$name]";
//Generally, label and title are always the same
$_data['title'] = $_data['label'];
//If no new value exists, use the existing slide data.
if (!array_key_exists('value', $_data)) {
$_data['value'] = $this->_getSlide()->getData($name);
}
//Finally, call vanilla funcctionality to add field.
$fieldset->addField($name, $_data['input'], $_data);
}
return $this;
}
}
I'm not sure if you need it, but here's my file structure as well
Namespace
-Slides
--Block
---Adminhtml
----Slide
-----Edit
------Form.php
-----Edit.php
-----Grid.php
----Slide.php
--controllers
---Adminhtml
----SlideConroller.php
--etc
---config.xml
--Helper
---Data.php
--Model
---Resource
----Slide
-----Collection.php
----Slide.php
---Slide.php
--sql
---namespace_slides_setup
----install-0.0.1.php
The issue is that Magento cannot find your head block.
Instead of using:
$this->getLayout()->getBlock('head')->setCanLoadTinyMce(true);
Try calling it like this:
Mage::app()->getLayout()->getBlock('head')->setCanLoadTinyMce(true);
If that doesn't work, it's a different issue but the problem is still that Magento can't find the head block.
I imagine you no longer need a solution for this, but I ran into the same issue using the same tutorial that you did.
The 'head' block (and thus setCanLoadTinyMce()) was unavailable in the Edit.php and the Form.php via the _prepareLayout() function.
The 'head' block was available in the controller(SlideController.php in your case) between $this->loadLayout() and $this->renderLayout within the editAction() function.
In SlideController.php change
$this->loadLayout()
->_addContent($brandBlock)
->renderLayout();
to
$this->loadLayout() ;
$this->_addContent($brandBlock);
$this->getLayout()->getBlock('head')->setCanLoadTinyMce(true);
$this->renderLayout();

Categories