Adding a button to the CMS in SilverStripe - button

How do I add a button to the backend of the CMS that fires an action? I can display the button where I want using:
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->addFieldsToTab("Root.ButtonTest", array(
FormAction::create('doAction', 'Action button')
)
);
return $fields;
}
public function doAction()
{
//Do something
}
However the button added does nothing when clicked.
I've seen one example of how to put a button on the main action bar (next to save/publish) but that's not what I'm trying to do.
Looking at the only page of documentation I can find, do I need to do something within:
public function getCMSActions()
{
$actions = parent::getCMSActions();
//Something here?
}
It isn't very clear how to create the action that the button calls.

You'll have to extend/decorate LeftAndMain with your own extension and the action you want to call. Here's an example:
<?php
class MyExtension extends LeftAndMainExtension
{
private static $allowed_actions = array(
'doAction'
);
public function doAction($data, $form){
$className = $this->owner->stat('tree_class');
$SQL_id = Convert::raw2sql($data['ID']);
$record = DataObject::get_by_id($className, $SQL_id);
if(!$record || !$record->ID){
throw new SS_HTTPResponse_Exception(
"Bad record ID #" . (int)$data['ID'], 404);
}
// at this point you have a $record,
// which is your page you can work with!
// this generates a message that will show up in the CMS
$this->owner->response->addHeader(
'X-Status',
rawurlencode('Success message!')
);
return $this->owner->getResponseNegotiator()
->respond($this->owner->request);
}
}
Once you have written an extension like this, you'll have to apply it to LeftAndMain by adding the following to your mysite/_config/config.yml:
LeftAndMain:
extensions:
- MyExtension
That's it. Your doAction button should now actually do something!

Not sure if this is helpful, but here's how you can add action-buttons to a ModelAdmin.
(does reload the page)
...in the admin class:
public function getEditForm($id = null, $fields = null)
{
$form = parent::getEditForm($id, $fields);
$form
->Fields()
->fieldByName($this->sanitiseClassName($this->modelClass))
->getConfig()
->getComponentByType('GridFieldDetailForm')
->setItemRequestClass('MyGridFieldDetailForm_ItemRequest');
return $form;
}
MyGridFieldDetailForm_ItemRequest.php
class MyGridFieldDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
{
function ItemEditForm()
{
$form = parent::ItemEditForm();
$formActions = $form->Actions();
$button = FormAction::create('myAction');
$button->setTitle('button label');
$button->addExtraClass('ss-ui-action-constructive');
$formActions->push($button);
$form->setActions($formActions);
return $form;
}
public function myAction(){ //do things }
}

Related

Wordpress seems to not execute function in add_action

I just start with Wordpress plugin and I try some codes to understand Wordpress plugin.
I try to display a custom message when I activate and deactivate my plugin.
The message for activation works very well but I have no message during deactivation.
Transient value changes but no message when I deactivate the plugin.
I thought add_action sets a function to be executed on every admin action on the plugin.... Am I wrong?
Can you help me? Thanks
class DuplicatePostPlugin
{
const DUPLICATE_POST_ACTIVATED = 'duplicate_post_activated';
public function __construct(string $file) {
register_activation_hook($file, [$this, 'plugin_activation_callback']);
register_deactivation_hook($file, [$this, 'plugin_deactivation_callback']);
add_action('admin_notices', [$this, 'notice_activation_callback']);
}
public function plugin_activation_callback() : void {
set_transient(self::DUPLICATE_POST_ACTIVATED, 1);
}
public function plugin_deactivation_callback() : void {
set_transient(self::DUPLICATE_POST_ACTIVATED, 0);
//do_action('admin_notices');
}
public function notice_activation_callback() : void {
if (get_transient(self::DUPLICATE_POST_ACTIVATED) == 1) {
self::render('notices', [ 'message' => "Activation OK"]);
} else if (get_transient(self::DUPLICATE_POST_ACTIVATED) == 0){
self::render('notices', [ 'message' => "Deactivation OK"]);
}
//delete_transient(self::DUPLICATE_POST_ACTIVATED);
}
public static function render(string $name, array $args = []) : void {
extract($args);
$file = DUPLICATE_POST_PLUGIN_DIR . "views/$name.php";
ob_start();
include_once($file);
echo ob_get_clean();
}

Silverstripe 4 show specific records in GridField

I created a module that will allow certain user groups to create a request in the CMS.Any logged in user can access the module.
When user visit 'RequestModeule' in in CMS section in GRID CMS FIELDS I want to show him the requests he only sent not another customer requests. The system displays the requirements of all users and he see all.
How do I set the CMS FIELDS results to be displayed for the currently logged in user.
This is a ecommerce site and some customers want to return the product, some want to replace the product, some want to complain about the quality. I want to allow customers to submit a request themselves. I started with a simple model, so I will expand it when I solve big problems.
Here is my Model.
class Request extends DataObject
{
private static $db = [
'FullName' => 'Varchar',
'RequestText' => 'Text'
];
private static $has_one = [
'Photo' => Image::class
];
public function canView($member = null)
{
return Permission::check('VIEW_REQUEST', 'any', $member);
}
public function canEdit($member = null)
{
return Permission::check('EDIT_REQUEST', 'any', $member);
}
public function canDelete($member = null)
{
return Permission::check('DELETE_REQUEST', 'any', $member);
}
public function canCreate($member = null, $context = [])
{
return Permission::check('CREATE_REQUEST', 'any', $member);
}
/**
* #return \SilverStripe\Forms\FieldList
*/
public function getCMSFields()
{
$fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Main', TextField::create('FullName'));
$fields->addFieldToTab('Root.Main', TextField::create('RequestText'));
$fields->addFieldToTab('Root.Photos', $img = UploadField::create('Photo', 'Main photo'));
$img->setFolderName("products");
$img->setAllowedExtensions(["png", "jpg", "jpeg", "gif", "webp"]);
return $fields;
}
}
Request Controller
class Request_Controller extends ContentController implements PermissionProvider
{
function providePermissions(){
return array(
"CREATE_REQUEST" => "Create REQUEST",
"EDIT_REQUEST" => "Edit REQUEST",
"VIEW_REQUEST" => "View REQUEST",
"DELTE_REQUEST" => "Delete REQUEST",
);
}
First of all, I don't see anything about connecting your Request class with a Member. If you want to only show the things a member has created, you need to somehow store the Members ID or something.
The most obvious way to do this would be a private static $has_one = ['CreatedBy' => Member::class].
(and during creation you'll have to put the member ID into CreatedBy. For example you can do protected function onBeforeWrite() { parent::onBeforeWrite(); $this->CreatedByID = Security::getCurrentUser()->ID; })
As for the GridField:
There are 2 ways to control what is displayed in a GridField:
GridFields 3rd argument is any SS_List instance, so you can filter or customize that any way you want. For example you can use Request::get()->filter('CreatedByID' => 13) to only show the objects created by member ID 13.
You can use canView on each Request object. If it returns false, it will not be shown. So you can do public function canView($member = null) { return $member && $member->ID === $this->CreatedByID && Permission::check('VIEW_REQUEST', 'any', $member); }

SilverStripe: How to set/ specify the icon for GridField custom action button

I am working on a SilverStripe project. In my project, I am trying to create a GridField custom button. I followed the official documentation. Following is the class form the SilverStripe official page.
class GridFieldCustomAction implements GridField_ColumnProvider, GridField_ActionProvider, GridField_ActionMenuItem
{
public function getTitle($gridField, $record, $columnName)
{
return 'Custom action';
}
public function getCustomAction($gridField, $record)
{
if (!$record->canEdit()) {
return;
}
return GridField_FormAction::create(
$gridField,
'CustomAction'.$record->ID,
'Custom action',
"docustomaction",
['RecordID' => $record->ID]
)->addExtraClass(
'action-menu--handled'
);
}
public function getExtraData($gridField, $record, $columnName)
{
$field = $this->getCustomAction($gridField, $record);
if (!$field) {
return;
}
return $field->getAttributes();
}
public function getGroup($gridField, $record, $columnName)
{
return GridField_ActionMenuItem::DEFAULT_GROUP;
}
public function augmentColumns($gridField, &$columns)
{
if (!in_array('Actions', $columns)) {
$columns[] = 'Actions';
}
}
public function getColumnAttributes($gridField, $record, $columnName)
{
return ['class' => 'grid-field__col-compact'];
}
public function getColumnMetadata($gridField, $columnName)
{
if ($columnName === 'Actions') {
return ['title' => ''];
}
}
public function getColumnsHandled($gridField)
{
return ['Actions'];
}
public function getColumnContent($gridField, $record, $columnName)
{
$field = $this->getCustomAction($gridField, $record);
if (!$field) {
return;
}
return $field->Field();
}
public function getActions($gridField)
{
return ['docustomaction'];
}
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
{
if ($actionName !== 'docustomaction') {
return;
}
// perform your action here
// output a success message to the user
Controller::curr()->getResponse()->setStatusCode(
200,
'Do Custom Action Done.'
);
}
}
I am struggling to add the custom icon for the button or specify the style class of the button. I can change the column class name. But I cannot find a way for the button. How can I do that?
you can achieve this using ->setAttribute('classNames', 'font-icon-<your-icon>');
i.e. for "edit" icon the code would looks like this:
return GridField_FormAction::create(
$gridField,
'CustomAction'.$record->ID,
'Custom action',
"docustomaction",
[
'RecordID' => $record->ID
]
)
->addExtraClass('action-menu--handled')
->setAttribute('classNames', 'font-icon-edit');
You can find all available icons on this page:
https://gbaumeister.github.io/ss4-icons/

Disable action for specific entries

In Sonata Admin, in one of my section, I would like to disable the delete action for 4 specific entries/lines.
I know I can do it for the entire listing with configureRoutes() but couldn't find anything for specific entries.
I tried the following but getSubject() is always empty.
protected function configureRoutes(RouteCollection $collection)
{
$product = $this->getSubject();
if ($product && $product->getIsBase())
{
$collection->clearExcept(array('list', 'edit'));
}
}
Thanks
You have to override the isGranted method in your admin class:
public function isGranted($name, $object = null)
{
if (in_array($name, array('LIST', 'EDIT')) && $object && $object->getIsBase()) {
return false;
}
return parent::isGranted($name, $object);
}

How to generate a password automatically and fill in password fields when creating a member in the CMS?

I have created a Model Admin called 'Clients'. Under the "Security" tab I created a new group called 'clients'. This Model Admin is managing just the clients and not other members.
When creating a new member in the CMS using a model admin, I want to automatically generate a password for them (instead of them having to create their own one) one for them and then email it to them.
What I want to happen:
After the staff member clicks "Add member" the password and password confirmation textboxs are automatically populated with the generated password. - This is the most ideal way I believe. - Then once the staff member clicks save it will send the client and email with the username and newly generated password.
Question is how do you do this?
ClientAdmin.php
<?php
class ClientAdmin extends ModelAdmin {
private static $menu_icon = 'themes/cadence/images/icons/person.png';
public $showImportForm = false;
private static $managed_models = array(
'Member'
);
private static $url_segment = 'clients';
private static $menu_title = 'Clients';
public function getList() {
$list = parent::getList();
$clientGroup = Group::get()->filter('code', 'clients')->first();
$list = $list->filter('Groups.ID', $clientGroup->ID);
return $list;
}
}
MemberClientExtension.php
<?php
class MemberClientExtension extends DataExtension implements PermissionProvider
{
private static $db = array(
);
public function providePermissions() {
return array(
'CLIENTS' => 'Can access the site as a client',
);
}
public function updateCMSFields(FieldList $fields) {
}
public function generatePasswordForClient(){
$plainPassword = $this->owner->create_new_password();
$encryptedPassword = $this->owner->encryptWithUserSettings($plainPassword);
// Need to set password in database here?
return $plainPassword;
}
public function sendClientWelcomeEmail() {
$email = new Email('email#gmail.com', 'email#gmail.com', 'New member sign up');
$email->setTemplate('NewClientSignUp');
$email->populateTemplate(array(
'Email' => $this->owner->Email,
'Password' => $this->generatePasswordForClient()
));
return $email->send();
}
public function onBeforeWrite()
{
parent::onBeforeWrite();
}
public function onAfterWrite()
{
parent::onAfterWrite();
// Seems to send 2x emails.. Only want to send one
$this->sendClientWelcomeEmail();
}
}
You should set temporary plain text password in SetPassword field, and manage the context when onBeforeWrite and onAfterWrite hooks are called.
class MemberClientExtension extends DataExtension
{
protected $sendWelcomeEmail = false;
...
// onBeforeWrite on extension is called after password is encrypted and set
public function validate(ValidationResult $validationResult) {
if (!$this->owner->isInDB()) {
$this->sendWelcomeEmail = true;
}
}
public function onAfterWrite() {
if ($this->sendWelcomeEmail) {
// reset for password change
$this->sendWelcomeEmail = false;
$password = $this->generatePasswordForClient();
$this->owner->changePassword($password);
$this->sendClientWelcomeEmail(array(
'Email' => $this->owner->Email,
'Password' => $password;
));
}
}
}
You could use the populateDefaults() function in your Member extension.
public function populateDefaults() {
$this->owner->changePassword($this->generatePasswordForClient());
}
But let me say this: This is a bad idea. You don't want to send plain text passwords over something as insecure as an email. To let the user to choose its own password is by far the better way.

Resources