This forum has moved to a new location and is in read-only mode. Please visit talk.octobercms.com to access the new location.
Hello, I'm new to OctoberCMS, managed to create some plugins and override some other plugins partials but I have now a more complex problem to handle.
I'm using rainlab-page for building static pages and martin-forms for creating forms.
I need to display a contact form in the footer (of all pages) which includes a profile picture of a person to contact, that person being specific for every static pages, and should send the contact form to that person by email.
I set up a custom plugin that allow to create Contacts in the back-office, now I want to be able, for each static pages created with rainlab-page, to pick a Contact from that list, preferably by adding it to the top-level parameters (like Title and URL), as a dropdown list of Contacts.
If I edit the plugins/rainlab/pages/classes/page/fields.yaml I can add a "viewBag[contact]" and it will appear where it should, now how to make that happen by replacing the plugin by a custom plugin extending rainlab-pages and make the field generate a select list from my custom plugin's Contact model.
Next step, how to extend martin-forms plugin to set the Notification Settings -> Recipients to the Contact set in the Static page.
Do I even need to create custom plugins or can I just override everything needed from the theme?
The Empty AJAX Form component is inserted in the layout page, and referenced in the footer.htm partial.
Any lead would be greatly appreciated! Thanks
Last updated
I managed to do some of the requirements:
In my Contact plugin, in the Plugin class I add a boot method:
public function boot()
{
Page::extend(function ($model) {
$model->addDynamicMethod('getContactOptions', function () {
return Contact::all()->pluck('company_name');
});
});
Event::listen('backend.form.extendFields', function (WidgetBase $widget) {
// Only for the Pages controller
if (!$widget->getController() instanceof \RainLab\Pages\Controllers\Index) {
return;
}
$widget->addFields([
'viewBag[contact]' => [
'label' => 'Contact',
'type' => 'dropdown',
'options' => 'getContactOptions',
]
]);
});
}
This adds the select field after the other fields (Title, URL and the actions like preview, save, etc.).
It is properly persisted in the page viewBag upon saving.
Then in /themes/mytheme/partials/contactForm/default.htm
which is the partial I created to override martin-forms
plugin's partial (the martin-forms plugin's component "Empty AJAX Form" inserted in my layout has the "contactForm" alias), I add a onStart
function in the PHP section:
description = "contact-form-override"
==
function onStart()
{
$this->contact = Boboy\Contact\Models\Contact::find($this->contact + 1);
}
==
<form data-request="{{ __SELF__ }}::onFormSubmit">
{{ form_token() }}
<input type="hidden" name="contact" value="{{ this.page.contact.email }}" />
<div id="{{ __SELF__ }}_forms_flash"></div>
Then I'm able to insert a hidden input field that has the associated contact's email as its value.
So most of the work here is done but now I still don't know how to override martin-forms plugin to fetch the mail recipient from the form's content.
This seems to be the trickiest part, if any experienced OctoberCMS developer could shed some light on this for me that would make my day.
Cheers!
Just use "onFormSubmit" in the contact form partial for the ajax handler and create a dynamic method for this handler in your page to override the recipient in the component before calling the component onFormSubmit handler.
something like
function onFormSubmit()
{
$this->contactForm->setProperty('mail_recipients', [$this->contact->email] ) ;
return $this->contactForm->onFormSubmit();
}
Last updated
Thanks for your help! I actually managed to tap into the martin.forms.beforeSaveRecord in the boot method of the plugin, like so:
Event::listen('martin.forms.beforeSaveRecord', function ($post, $elt) {
$contact = Contact::where('email', $post['contact'])->first();
if ($contact) {
if (!isset($elt->getProperties()['mail_recipients'])) {
$recipientList = [];
} else {
$recipientList = $elt->getProperties()['mail_recipients'];
}
array_push($recipientList, $post['contact']);
$elt->setProperty('mail_recipients', $recipientList);
} else {
throw new AjaxException([
'#' . $elt->alias . '_forms_flash' => $elt->renderPartial($elt->property('messages_partial', '@flash.htm'), [
'status' => 'error',
'type' => 'danger',
'content' => 'This contact does not exist',
])]);
}
});
But your way is more straightforward, I added the event handler to the layout's PHP section (because the component is inserted at the layout level):
function onFormSubmit()
{
$contact = Boboy\Contact\Models\Contact::find($this->page->apiBag['staticPage']->viewBag['contact'] + 1);
$defaultContacts = $this->contactForm->getProperties()['mail_recipients'];
if (!in_array($contact, $defaultContacts)) {
array_push($defaultContacts, $contact);
$this->contactForm->setProperty('mail_recipients', $defaultContacts) ;
}
return $this->contactForm->onFormSubmit();
}
This is actually much better because I managed to tap into the viewBag of the page, which makes me able to directly fetch the Contact's id from the page viewBag, the email shouldn't have to go through the front-end back to the back-end (I had to check the existence of the email provided in the form to prevent injections of unrelated emails) and some staff members might want to keep their email secret, now it's perfectly safe.
Thanks again!
Instead of:
$defaultContacts = $this->contactForm->getProperties()['mail_recipients'];
you can use:
$defaultContacts = $this->contactForm->property('mail_recipients');
Oh okay, I tried "getProperty" and "properties", then took me an hour to find my way there, dumping the object in the response gives a HUGE result. I also forgot to check if 'mail_recipients' is null (when no recipients are set from the component's snippet), here is final code:
function onFormSubmit()
{
$contact = Boboy\Contact\Models\Contact::find($this->page->apiBag['staticPage']->viewBag['contact'] + 1);
$defaultContacts = $this->contactForm->property('mail_recipients');
if ($defaultContacts === null) {
$defaultContacts = [];
}
if (!in_array($contact, $defaultContacts)) {
array_push($defaultContacts, $contact);
$this->contactForm->setProperty('mail_recipients', $defaultContacts) ;
}
return $this->contactForm->onFormSubmit();
}
Thanks for the feedback!
Hello,
I now have a new problem.
When I try to go into the rainlab-pages plugin, on the Menus page, I get an error saying:
The model class RainLab\Pages\Classes\Menu must define a method getContactOptions() returning options for the 'viewBag[contact]' form field.
In both those type of page the Widget received is of type Backend\Widgets\Form
.
I don't know what seems to be the problem here, if I change my code to this:
$contactOptions = function ($model) {
$model->addDynamicMethod('getContactOptions', function () {
return Contact::all()->pluck('company_name');
});
};
Page::extend($contactOptions);
Menu::extend($contactOptions);
In order to add the method to both classes, I now get the error about the MenuItem class, problem is that this class does not have a extend
method.
Don't know what to do here.
make sure the model is a Page in your boot method when you add the event handler (Event::listen('backend.form.extendFields',...)
if (!$widget->getController() instanceof \RainLab\Pages\Controllers\Index ||
!$widget->model instanceof Page) {
return;
}
Solved the issue, thanks for your reactivity!
1-12 of 12