Wordpress plugins - divide in (OOP) Classes - wordpress

So, i am developing a Plugin, and I am using a main Class so it can be more easily managed:
class MyPlugin{
function __construct(){
//my add_actions here
add_action('template_redirect', array($this, 'template_redirect'));
}
//my_class_methods here
function template_redirect(){}
}
Up until now, this is all working just fine. But as the plot thickens, I need to add more and more complexity in the plugin so I would like to do something like:
create a new Class Comment
in MyPlugin's __construct instantiate $comment = new Comment()
delegate, in my template_redirect(), an action to a method in $comment like:
add_action('wp_insert_comment', array($this->comment,'wp_insert_comment'));
or:
create a new Class Comment
in MyPlugin's __construct instantiate $comment = new Comment()
in my Class Comment's __construct add the action to it's method comment_method:
add_action('wp_insert_comment', array($this, 'comment_method'));
Is this even possible? in either way, my behaviour isn't being called.
Thanks for your help

Yes, it's possible, but you should probably add most actions and filters with the plugins_loaded hook rather than using the template_redirect hook, which probably is not called at all when a comment is posted (the form goes to wp-comments-post.php, wordpress deals with request, then redirects the user back to the page where the comment form was before the template is needed.)
class MyPlugin{
function __construct(){
add_action("plugins_loaded", array($this,"_action_plugins_loaded"));
}
function _action_plugins_loaded(){
//add actions & filters here...
}
}

Related

Silverstripe 4 - Adding a FormAction via getCMSFields

Goal:
I have a DataObject called "Event". This is in a managed_model for "EventsAdmin" (extending ModelAdmin). When editing an Event, I want a tab on the record called "Moderation" that has a few fields and two buttons: "Approve" and "Reject". These two buttons call an action each that performs relevant actions.
Event extends DataObject
public function getCMSFields() {
$fields = parent::getCMSFields();
$eventStatus = $fields->dataFieldByName("EventStatus")
->setTitle('Current Status')
->setDisabled(true);
$approveButton = FormAction::create('doApproveEvent', _t('SiteBlockAdmin.Approve', 'Approve'))
->setUseButtonTag(true)
->addExtraClass('btn-outline-success font-icon-check-mark-circle');
$rejectButton = FormAction::create('doRejectEvent', _t('SiteBlockAdmin.Reject', 'Reject'))
->setUseButtonTag(true)
->addExtraClass('btn-outline-danger font-icon-cancel-circled');
$fields->addFieldsToTab('Root.Moderation', [
$eventStatus,
$approveButton,
$rejectButton
]);
return $fields;
}
This displays the buttons just fine. But they don't do anything. So I am trying to work out how they can plug into action methods doApproveEvent and doRejectEvent (And where they should go)
I did find docs that led me to adding the buttons to the action bar at the bottom of the CMS page via updateFormActions(). But this isn't what I want as the other fields I am adding above the buttons are part of the Approve/Reject process. Here is the code for this method. This works fine barring the buttons are not in a logical place for the process I'm trying to create.
class CMSActionButtonExtension extends DataExtension
{
public function updateFormActions(FieldList $actions)
{
$record = $this->owner->getRecord();
if (!$record instanceof Event || !$record->exists()) {
return;
}
$approveButton = FormAction::create('doApproveEvent', _t('SiteBlockAdmin.Approve', 'Approve'))
->setUseButtonTag(true)
->addExtraClass('btn-outline-success font-icon-check-mark-circle');
$rejectButton = FormAction::create('doRejectEvent', _t('SiteBlockAdmin.Reject', 'Reject'))
->setUseButtonTag(true)
->addExtraClass('btn-outline-danger font-icon-cancel-circled');
$actions->push($approveButton);
$actions->push($rejectButton);
}
public function doApproveEvent($data, $form) {
$record = $this->owner->getRecord();
// Approve logic
}
public function doRejectEvent($data, $form) {
$record = $this->owner->getRecord();
// Reject logic
}
}
The above Extension is attached to GridFieldDetailForm_ItemRequest
extension.yml
SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest:
extensions:
- My\Namespace\CMSActionButtonExtension
Interestingly, if I have both sets of buttons on the page at the same time, the updateFormActions option works while my desired option still doesn't. Despite the buttons being of identical markup and sitting inside the exact same form tag. I assume that has something to do with how Silverstripe loads the main content panel and the DOM.
Any thoughts on achieving this? Anyone seen a button added to the main CMS panel in a module that I could take a look at? I found this post from 5 years ago, but it's for SS3 and the answer doesn't work for me.
Short answer:
you have to add custom FormActions through an Extension on the Controller that controls the form (or on the form itself
Long Answer:
A bit of background on how SilverStripe does forms:
Generally speaking, forms are always served through Controllers/RequestHandlers (they need to be accessible on some route, usually that's an Action on a Controller that is often named Form, EditForm, ItemEditoForm, ...).
Fields
Inside the CMS you rarely ever have to create your own form, that's done by the CMSs built in Controllers/RequestHandlers for the admin area (GridFieldDetailForm_ItemRequest in this case).
Basically (pseudo code here), what those controllers do is:
public function EditForm() {
$fields = $myCurrentlyEditingDataObject->getCMSFields();
$actions = ...;
$validator = ...;
$this->updateFormActions(&$actions);
$form = new Form('ItemRequestForm', $fields, $actions, $validator);
$this->updateItemEditForm(&$form); // or $this->updateEditForm()
return $form;
}
So, getCMSFields() and in some cases getCMSActions()/getCMSValidator() (not sure if those 2 are still used in SilverStripe 4.x), you can add things to the form, without ever seeing the form object.
Also, the getCMSFields() will always be put into the ``` section of the Form, that's why your button is somewhere in the middle with all the fields and not with the other actions.
Submission
When a form is submitted (eg to /admin/pages/edit/EditForm/265/field/NameOfMyGridField/item/542/ItemEditForm), it will call the action GridFieldDetailForm_ItemRequest->ItemEditForm() which returns the Form object where subsequently FormRequestHandler->httpSubmission() is called. This will then look at the submitted data to figure out what action was clicked (eg $_REQUEST['action_doApproveEvent']) and try to find that action.
The way it tries to find that, is checking if it itself has a method called doApproveEvent, if that fails, it will try Form->getController()->doApproveEvent() or something like that. In the case of a GridField, that controller is GridFieldDetailForm_ItemRequest which means it will try to call GridFieldDetailForm_ItemRequest->doApproveEvent()
So, that means DataObject->getCMSFields() lets you easily add FormFields (and FormActions) into your form body.
But it does not provide a means of adding a method to handle the submission.
That's why, for custom actions you need to modify the Controller (GridFieldDetailForm_ItemRequest in this case).
You are doing this by creating a Extension which you attached to GridFieldDetailForm_ItemRequest.
Any method in your Extension is added to the thing it's attached to, so if you add a method called updateFormActions, it will kind of become GridFieldDetailForm_ItemRequest->updateFormActions().
And if you recall from earlier, the controller will call $this->updateFormActions() during the creation of the form.
Additionally, as I explained earlier, when a FormAction is named doApproveEvent it will look for a GridFieldDetailForm_ItemRequest->doApproveEvent(), which now exists because you added it through that Extension.
So, in summary: you have to add custom FormActions through an Extension on the Controller that controls the form (or on the form itself
PS: the old post from
bummzack you linked to worked in 3.x, because the Controller in his example that created the form was an instance of LeftAndMain.

How to use pluggables functions on plugins

I need to use functions such as wp_insert_user and wp_update_user in a plugin, but theses functions call others functions who are not yet loaded (such as get_user_by or get_userdata).
If I require the pluggable.php file where I use theses functions it works, but messing with WordPress load orders seems a pretty bad idea to me.
How would you use theses functions in a plugin ?
After looking at how ACF plugin do it, I've found a solution, instead of calling the function at the plugin load, I just add it to the action init.
require 'myClass.php';
$myClass = new myClass();
add_action('init', [$myClass, 'myFunction']);
I would even suggest to use a main class for your plugins and in the __construct hook your logic to init and/or plugins_loaded.
class MyPlugin {
public function __construct() {
add_action( 'plugins_loaded', [$this, 'plugins_loaded'] );
add_action( 'init', [$this, 'init'] );
}
public function plugins_loaded() {
// pluggables functions are available here
}
public function init() {
// pluggables functions are available here
}
}
new MyPlugin();

Wordpress Hook the_content Within Widget

I am writing a widget that list the headings in the post and then created hash links and edits the HTML to reflect that. I've got the list widget content figured out and I just need to edit the_content, i've tried to add a filter for the method that returns the updated code but it's not working.
What would be the best way to do this? My class is called post_headings_widget and the edited HTML content is stored within $this->the_content.
I was hoping I could do this within the widget class
public
function edited_content() {
return $this->the_content;
}
and then to edit the content output here
add_filter( 'the_content', [ 'post_headings_widget', 'edited_content' ] );
It calls the class method fine but i'm not sure exactly how it works so i'm guessing it called the method directly without calling the constructors etc?
I have also tried to just create a filter from within the widget() method but that did not work either, heres what I tried:
add_filter( 'the_content', function() {
return 'test';
} );
Any ideas on a solution?
You have to pass the_content as a parameter in your filter function/callback.
Check the Wordpress docs: https://codex.wordpress.org/Plugin_API/Filter_Reference/the_content
On widgets you need to bind on widget_text
add_filter('widget_text', 'se24265_my_function');
function se24265_my_function( $content )
{
# replace code here on widget $content
return $content;
}

decluttering UI, order Extension gets applied

A few years ago I made a SilverStripe website and added too many fields to Page.php. I'm reworking some of this at the moment but cannot afford do reinvent the Project - now on SilverStripe 3.1.10.
I thought to declutter the UI for Page Sub-Classes, that do not need all the inherited fields, with a few Extensions.
An example how this extension could look
class NoClutter extends Extension {
public function updateCMSFields(FieldList $fields) {
$fields->removeFieldFromTab("Root.Main", "MenuTitle");
$fields->removeFieldFromTab("Root.Main", "Workflow");
}
}
config.yml
RedirectorPage:
extensions:
- NoClutter
This works on all classes for fields added in SiteTree (such as the MenuTitle field), but not for fields added in Page (such as the Workflow field). If the Extension is on UserDefinedForm, Workflow is also removed. But it does not work if the extension is on RedirectorPage. MenuTitle on the other hand is removed in both classes. My guess it's about order. My project is After: 'framework/','cms/' and hope I can make an extension like NoClutter work within the project.
How can I achieve this or how else could I work around the problem?
You need to add $this->extend('updateCMSFields', $fields) at the end of your Page getCMSFields() function.
class Page extends SiteTree {
// ...
public function getCMSFields() {
// call updateCMSFields after adding your fields
SiteTree::disableCMSFieldsExtensions();
$fields = parent::getCMSFields();
SiteTree::enableCMSFieldsExtensions();
// ...
$this->extend('updateCMSFields', $fields);
return $fields;
}
}
$this->extend('updateCMSFields', $fields) declares where your code updateCMSFields() function will get called.
The problem you are having is updateCMSFields() is getting called before you add your custom fields in the Page getCMSFields() function. So you are trying to remove the Workflow field before it is added. This is because the updateCMSFields extension hook is declared in the parent SiteTree getCMSFields() function.
UserDefinedForm solves this by calling $this->extend('updateCMSFields', $fields) at the bottom of its getCMSFields(). SiteTree::disableCMSFieldsExtensions() is required before parent::getCMSFields() is called for the extension hook to work.

Gravity Forms gform_after_submission not working from a plugin?

I have a plugin that I've written that is trying to call the gforms_after_submission hook. For some reason it isn't calling the function. I see in Gravity Forms documentation that it says I have to call gform_after_submission from the functions file - is there any reason I can't call it from the plugin? I've tested with the mail function, and the function admin_init is triggering.
<?php
class Infusionsoft_GformsPDF {
public function __construct() {
add_action( 'admin_init', array( $this, 'admin_init' ) );
}
/**
* Should call my function, but doesn't
*/
public function admin_init() {
add_action('gform_after_submission', 'handle_file', 10, 2);
}
/**
* Get the file URL and post it to Infusionsoft
*/
public function handle_file($entry, $form){
mail('myemail#email.com', 'Handle File was triggered', 'yippee');
}
}
the problem here is that you've added the function call to the admin_init hook. The admin_init hook is only triggered when the user accesses the admin area, but you're submitting a form here, an action taking place on the front-end of your site outside of the admin area.
It's a simple fix :-) Just use the front-end initialization hook instead — init
Also check out this reference for the actions that are typically run when a page is loaded, on the front of your site and the admin area:
http://codex.wordpress.org/Plugin_API/Action_Reference

Resources