How do I display an error in the form, a flash message or someway, indicating what he did wrong?
The code is something like this:
class My_Widget extends WP_Widget {
public function update( $new_instance, $old_instance ) {
if (!valid($new_instance))
// How do I notify the user with a custom message
return false;
else
return $new_instance
}
}
I know that returning false prevents the options from being saved, but the user won't know why.
Try this one,
public function update( $new_instance, $old_instance ) {
$old_instance['errors'] = array();
if (!valid($new_instance)) {
$$old_instance['errors']['myfield'] = 'Custom error mesasage goes here';
// How do I notify the user with a custom message
return false;
//return $old_instance;
}
else
return $new_instance
}
public function form($instance) {
$myfieldMsg = (isset($instance['errors']) && isset($instance['errors']['myfield'])) ? $instance['errors']['myfield']) : null;
echo $myfieldMsg;
....
Related
I have a CrudController for my entity, Participant. I want to add a custom action, sendAcknowledgementEmail. The EasyAdmin docs doesn't mention anything about the custom function parameters or return values.
I have the following code
public function configureActions(Actions $actions): Actions
{
$send_acknowledgement_email = Action::new('sendAcknowledgementEmail', 'Send Acknowledgement Email', 'fa fa-send')
->linkToCrudAction('sendAcknowledgementEmail');
return $actions
->add(Crud::PAGE_INDEX, $send_acknowledgement_email)
->add(Crud::PAGE_EDIT, $send_acknowledgement_email)
;
}
public function sendAcknowledgementEmail() //Do I need parameters?
{
//How do I get the Entity?
//What should I return?
}
So far, EasyAdmin detects the custom function but I get an error "The controller must return a "Symfony\Component\HttpFoundation\Response" object but it returned null. Did you forget to add a return statement somewhere in your controller?"
How do I continue from here?
The v3.x of the bundle is quite new and the documentation is not perfect yet.
Based on Ceochronos answer, here is my implementation for a clone action.
public function configureActions(Actions $actions): Actions
{
$cloneAction = Action::new('Clone', '')
->setIcon('fas fa-clone')
->linkToCrudAction('cloneAction');
return $actions
->add(Crud::PAGE_INDEX, $cloneAction);
}
public function cloneAction(AdminContext $context)
{
$id = $context->getRequest()->query->get('entityId');
$entity = $this->getDoctrine()->getRepository(Product::class)->find($id);
$clone = clone $entity;
// custom logic
$clone->setEnabled(false);
// ...
$now = new DateTime();
$clone->setCreatedAt($now);
$clone->setUpdatedAt($now);
$this->persistEntity($this->get('doctrine')->getManagerForClass($context->getEntity()->getFqcn()), $clone);
$this->addFlash('success', 'Product duplicated');
return $this->redirect($this->get(CrudUrlGenerator::class)->build()->setAction(Action::INDEX)->generateUrl());
}
After browsing through the EasyAdmin AbstractCrudController I came up with the following working code.
In order to get the current object you need the parameter AdminContext
For my use case I want to return to the CrudController index action, so for that I can do a redirect.
Note: you need to inject the CrudUrlGenerator service in your constructor controller.
public function sendAcknowledgementEmail(AdminContext $context)
{
$participant = $context->getEntity()->getInstance();
// Your logic
$url = $this->crudUrlGenerator->build()
->setController(ParticipantCrudController::class)
->setAction(Action::INDEX)
->generateUrl();
return $this->redirect($url);
}
My current function looks like this:
public function sendAcknowledgementEmail(AdminContext $context)
{
$participant = $context->getEntity()->getInstance();
$participant->sendAcknowledgementEmail();
$this->addFlash('notice','<span style="color: green"><i class="fa fa-check"></i> Email sent</span>');
$url = $this->crudUrlGenerator->build()
->setController(ParticipantCrudController::class)
->setAction(Action::INDEX)
->generateUrl();
return $this->redirect($url);
}
My current working code
<?php
namespace App\Controller\Admin;
use App\Service\WebinarService;
use EasyCorp\Bundle\EasyAdminBundle\Router\CrudUrlGenerator;
use Symfony\Contracts\Translation\TranslatorInterface;
// ...
class ParticipantCrudController extends AbstractCrudController
{
private CrudUrlGenerator $crudUrlGenerator;
private WebinarService $webinar_service;
private TranslatorInterface $translator;
public function __construct(CrudUrlGenerator $crudUrlGenerator, WebinarService $webinar_service, TranslatorInterface $translator)
{
$this->crudUrlGenerator = $crudUrlGenerator;
$this->webinar_service = $webinar_service;
$this->translator = $translator;
}
// ...
public function sendAcknowledgementEmail(AdminContext $context): Response
{
$participant = $context->getEntity()->getInstance();
try {
$this->webinar_service->sendAcknowledgementEmail($participant);
$this->addFlash('notice', 'flash.email.sent');
} catch (Exception $e) {
$this->addFlash('error', $this->translator->trans('flash.error', ['message' => $e->getMessage()]));
}
$url = $this->crudUrlGenerator->build()
->setController(ParticipantCrudController::class)
->setAction(Action::INDEX)
->generateUrl()
;
return $this->redirect($url);
}
}
I'm developing a custom wordpress widget. The widget needs some data from the user and needs to check this data server-side.
I wrote the code that checks the data inside the function 'update' of the widget. When I press the button save of the widget the function update got called correctly and my validation is executed.
public function update( $new_instance, $old_instance ) {
$instance = array();
foreach ($this->fields as $field) {
$fieldName = $field['name'];
$instance[$fieldName] =
(!empty($new_instance[$fieldName]) strip_tags($new_instance[$fieldName]) : '' );
}
$check = validate($new_instance);
return $instance;
}
What I need is to display a message to the user based on the result of the validation. How can I do this? For what I've seen the function update is called through ajax so I can't use an admin notice.
Is it possible?How can I do that?
Try below code
add_action('admin_notices', 'misha_custom_order_status_notices');
function misha_custom_order_status_notices() {
global $pagenow, $typenow;
if( get_transient( 'fx-admin-notice-panel' )){
echo "<div class=\"updated\"><p>Custom notification comes here</p></div>";
}
}
public function update( $new_instance, $old_instance ) {
$instance = array();
foreach ($this->fields as $field) {
$fieldName = $field['name'];
$instance[$fieldName] =
(!empty($new_instance[$fieldName])?
strip_tags($new_instance[$fieldName]) :
''
);
}
$check = validate($new_instance);
set_transient( 'fx-admin-notice-panel', true, 5 );
return $instance;
}
I'm trying to check if WooCommerce is active or not, I created a property with with a default value of false, then I created a method to check if WooCommerce is active using is_plugin_active() and admin_init hook, if active the value of the property must be updated to true: here is the code:
class MyClass{
public $woo_active = false;
public function __construct(){
add_action( 'admin_init', array( $this, 'check_if_woo_active' ) );
}
// check if WooCommerce is active
public function check_if_woo_active(){
if( is_plugin_active( 'woocommerce/woocommerce.php' ) ){
$this->woo_active = true;
}
}
// is_woo_active()
public function is_woo_active(){
return $this->woo_active;
}
}
$var = new MyClass();
var_dump( $var->is_woo_active() );
the issue is that var_dump returns false even if WooCommerce is active, BUT, if I use var_dump inside the function check_if_woo_active(), it returns true.
Why the property value is not updated? thanks
Updated:
The Second Solution as #helgatheviking sugested works fine, also this works very well and short
class MyClass{
// check if WooCommerce is active
public function is_woo_active(){
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
if( is_plugin_active( 'woocommerce/woocommerce.php' ) ){
return true;
}else{
return false;
}
}
}
$var = new MyClass();
var_dump( $var->is_woo_active() );
If I had to guess, then $var = new MyClass(); is run before admin_init so the check_if_woo_active() isn't run.
Couple things you could do. First, I will usually launch my plugin on the woocommerce_loaded hook. That way I am 100% sure WooCommerce is running.
class MyClass{
protected static $instance = null;
/**
* Main MyClass Instance
*
* Ensures only one instance of MyClass is loaded or can be loaded.
*
* #static
* #see MyClass()
* #return MyClass - Main instance
* #since 0.1.0
*/
public static function instance() {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof MyClass ) ) {
self::$instance = new MyClass();
}
return self::$instance;
}
public function __construct(){
// Do what you want, WC is definitely active
}
}
/**
* Returns the main instance of class.
*
* #return MyClass
*/
function MyClass() {
return MyClass::instance();
}
// Launch the class if WooCommerce is loaded:
add_action( 'woocommerce_loaded', 'MyClass' );
You could also mimic what WooCommerce does with their premium plugins and check the option that stores the active plugins:
class MyClass{
private static $active_plugins;
public static function get_active_plugins() {
self::$active_plugins = (array) get_option( 'active_plugins', array() );
if ( is_multisite() ){
self::$active_plugins = array_merge( self::$active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
}
}
// check if WooCommerce is active
public static function check_if_woo_active() {
if ( ! self::$active_plugins ) self::get_active_plugins();
return in_array( 'woocommerce/woocommerce.php', self::$active_plugins ) || array_key_exists( 'woocommerce/woocommerce.php', self::$active_plugins );
}
}
var_dump( MyClass::check_if_woo_active() );
I'm trying to implement a custom Voter.
From the controller I call it this way:
$prj = $this->getDoctrine()->getRepository('AppBundle:Project')->findOneById($id);
if (false === $this->get('security.authorization_checker')->isGranted('responsible', $prj)) {
throw new AccessDeniedException('Unauthorised access!');
}
The first line properly retrieves the Project object (I checked with a dump).
The problem occurs inside the voter
<?php
namespace AppBundle\Security\Authorization\Voter;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class ProjectVoter implements VoterInterface
{
const RESPONSIBLE = 'responsible';
const ACCOUNTABLE = 'accountable';
const SUPPORT = 'support';
const CONSULTED = 'consulted';
const INFORMED = 'informed';
public function supportsAttribute($attribute)
{
return in_array($attribute, array(
self::RESPONSIBLE,
self::ACCOUNTABLE,
self::SUPPORT,
self::CONSULTED,
self::INFORMED,
));
}
public function supportsClass($class)
{
$supportedClass = 'AppBundle\Entity\Project';
return $supportedClass === $class || is_subclass_of($class, $supportedClass);
}
/**
* #var \AppBundle\Entity\Project $project
*/
public function vote(TokenInterface $token, $project, array $attributes)
{
// check if class of this object is supported by this voter
if (!$this->supportsClass(get_class($project))) {
return VoterInterface::ACCESS_ABSTAIN;
}
// check if the voter is used correct, only allow one attribute
// this isn't a requirement, it's just one easy way for you to
// design your voter
if (1 !== count($attributes)) {
throw new \InvalidArgumentException(
'Only one attribute is allowed'
); //in origin it was 'for VIEW or EDIT, which were the supported attributes
}
// set the attribute to check against
$attribute = $attributes[0];
// check if the given attribute is covered by this voter
if (!$this->supportsAttribute($attribute)) {
return VoterInterface::ACCESS_ABSTAIN;
}
// get current logged in user
$user = $token->getUser();
// make sure there is a user object (i.e. that the user is logged in)
if (!$user instanceof UserInterface) {
return VoterInterface::ACCESS_DENIED;
}
$em = $this->getDoctrine()->getManager();
$projects = $em->getRepository('AppBundle:Project')->findPrjByUserAndRole($user, $attribute);
foreach ($projects as $key => $prj) {
if ($prj['id'] === $project['id'])
{
$granted = true;
$index = $key; // save the index of the last time a specifif project changed status
}
}
if($projects[$index]['is_active']===true) //if the last status is active
return VoterInterface::ACCESS_GRANTED;
else
return VoterInterface::ACCESS_DENIED;
}
}
I get the following error
Attempted to call method "getDoctrine" on class
"AppBundle\Security\Authorization\Voter\ProjectVoter".
I understand that the controller extends Controller, that is why I can use "getDoctrine" there. How can I have access to my DB from inside the Voter?
I solved it. This is pretty curious: I spend hours or days on a problem, then post a question here, and I solve it myself within an hour :/
I needed to add the following in my voter class:
public function __construct(EntityManager $em)
{
$this->em = $em;
}
I needed to add the following on top:
use Doctrine\ORM\EntityManager;
I also needed to add the arguments in the service.yml
security.access.project_voter:
class: AppBundle\Security\Authorization\Voter\ProjectVoter
arguments: [ #doctrine.orm.entity_manager ]
public: false
tags:
- { name: security.voter }
Here is my problem.
I am curently learning Symfony and I have created a form with a formType file and a formHandler.
I'd like now to use values of the form in my handler but I can't figure how to call those values, which method can I use? I've tried many method of the request class but it doesn't work.
Could you help me please?
Here is my handler. Some of my try are still in it commented, it's quiet simple, I'm just trying to do an echo.
class adminFormHandler
{
protected $form;
protected $request;
protected $em;
public function __construct(Form $form, Request $request, EntityManager $em)
{
$this->form = $form;
$this->request = $request;
$this->em = $em;
}
public function process()
{
if( $this->request->getMethod() == 'POST' )
{
$this->form->bindRequest($this->request);
//if( $this->form->isValid() )
//{
//echo $this->request->get('nom');
//$param = $this->request->request->keys();
//$param = $this->form->getData(nom);
//echo $param;
$myfield = $form->getAttribute('nom');
echo $myfield->getData();
//echo $param;//$this->form->get('nom')->getText();
//$this->onSuccess($this->form->getData());
return true;
//}
}
return false;
}
public function onSuccess1(/*Students $students*/)
{
//$this->em->persist($students);
//$this->em->flush();
echo'on success 1';
}
public function onSuccess2()
{
//$this->em->persist($students);
//$this->em->flush();
echo'on success 2';
}
}
You can use:
$data = $this->form->getData();
$myfield = $data['nom'];
or
$myfield = $this->form->get('nom')->getData();