For the past couple months I have been pulling hair out trying to get forms to work with Staticpublisher module for Silverstripe.
The form is built properly on the frontend but does not send emails or redirect to the success page when static publisher is enabled.
I have set up the form as follows:
Page.php
class Page extends SiteTree {
............
public function allPagesToCache() {
$urls = array();
$pages = DataObject::get("Page");
// ignored page types
$ignored = array(
'EnquiryForm'
);
foreach($pages as $page) {
if (!in_array($page->Classname, $ignored)) {
$urls = array_merge($urls, (array)$page->subPagesToCache());
}
}
return $urls;
}
public function subPagesToCache() {
$urls = array();
$urls[] = $this->Link();
if ($this->ProvideComments) {
$urls[] = Director::absoluteBaseURL() . "CommentingController/rss/SiteTree/" . $this->ID;
}
return $urls;
}
public function pagesAffectedByChanges() {
$urls = $this->subPagesToCache();
if($p = $this->Parent) $urls = array_merge((array)$urls, (array)$p->subPagesToCache());
return $urls;
}
}
............
class Page_Controller extends ContentController {
private static $allowed_actions = array('EnquiryForm');
public function init() {
parent::init();
}
public function EnquiryForm() {
return new EnquiryForm($this, 'EnquiryForm');
}
}
EnquiryForm.php
class EnquiryForm extends Form {
public function __construct($controller, $name) {
// Form fields
$fields = new FieldList(
TextField::create('Name')->setAttribute('placeholder', 'Name'),
EmailField::create('Email')->setAttribute('placeholder', 'Email'),
TextField::create('Phone')->setAttribute('placeholder', 'Phone'),
TextareaField::create('Message')->setAttribute('placeholder', 'Message')
);
// Strict method check
$strictFormMethodCheck = true;
// Form action
$actions = new FieldList(
new FormAction('sendEmail', 'Submit')
);
// Required fields
$validator = new RequiredFields('Name', 'Email', 'Phone', 'Message');
parent::__construct($controller, $name, $fields, $actions, $validator);
}
public function sendEmail($data) {
// Submit function for contact form above
if (isset($data) && !empty($data)) {
// Phone number default
$visitorPhone = !empty($data['Phone']) ? $data['Phone'] : '(Not supplied)';
// Get clients email from the CMS
$clientEmail = SiteConfig::current_site_config()->Email;
// Extra emails to send to (CC)
$ccEmails = SiteConfig::current_site_config()->CCEmails;
// Setup email
$email = new Email();
$email->setTo($clientEmail);
$email->setCc($ccEmails);
$email->setFrom($data['Email']);
$email->setSubject("New website enquiry received from " . ucwords($data["Name"]));
// Email message
$messageBody = "
<h1>Enquiry Via The " . SiteConfig::current_site_config()->Title . " Website</h1>
<h2>Details</h2>
<ul>
<li><strong>Name:</strong> " . $data["Name"] . "</li>
<li><strong>Email:</strong> <a href='mailto:{$data['Email']}'>{$data['Email']}</a></li>
<li><strong>Phone:</strong> " . $visitorPhone . "</li>
</ul>
<h2>Message</h2>
<p>{$data['Message']}</p>
";
$email->setBody($messageBody);
$email->send();
SESSION::set('Post', $data);
$customSuccessPage = $this->EnquirySuccessPage()->Link();
Controller::curr()->redirect($successLink);
}
}
public function forTemplate() {
return $this->renderWith(array($this->class, 'Form'));
}
}
I am then including the form in various templates with {$EnquiryForm}.
I would appreciate any help with this greatly.
Try:
Place the sendEmail() function within Page_Controller
add 'sendEmail' to the $allowed_actions of the Page_Controller class
Replace Controller::curr()->redirect with $this->redirect
Within the EnquiryForm function replace: return new EnquiryForm($this, 'EnquiryForm'); with return new EnquiryForm($this, 'sendEmail');
Try updating the allowed_action with the below mentioned code. And also rebuild dev/build the site
private static $allowed_actions = array('EnquiryForm'.'sendEmail');
Related
I code a class for Hook system. But this is outdated. I want to use splObserver to code it.
<?php
class Event
{
private static $filters = [];
private static $actions = [];
public static function addAction($name, $callback, $priority = 10)
{
if (! isset(static::$actions[$name])) {
static::$actions[$name] = [];
}
static::$actions[$name][] = [
'priority' => (int)$priority,
'callback' => $callback,
];
}
public function doAction($name, ...$args)
{
$actions = isset(static::$actions[$name]) ? static::$actions[$name] : false;
if (! $actions) {
return;
}
// sort actions by priority
$sortArr = array_map(function ($action) {
return $action['priority'];
}, $actions);
\array_multisort($sortArr, $actions);
foreach ($actions as $action) {
\call_user_func_array($action['callback'], $args);
}
}
}
Event::addAction('action1', function(){
echo 'balabala1';
});
Event::addAction('action1', function(){
echo 'balabala2';
});
Event::doAction('action1');
Output: balabala1 balabala2
It works good.
I want to use SplObserver to re-code it and try to code but no idea.
I don't really know whether this implementation could be useful in a real life application or not but, for the sake of answering your question, here we go...
Let's imagine we have a User class that we'd like to hook with our custom functions.
First, we create a reusable trait containing the Subject logic, capable of managing "event names" to whom we can hook our actions.
trait SubjectTrait {
private $observers = [];
// this is not a real __construct() (we will call it later)
public function construct()
{
$this->observers["all"] = [];
}
private function initObserversGroup(string $name = "all")
{
if (!isset($this->observers[$name])) {
$this->observers[$name] = [];
}
}
private function getObservers(string $name = "all")
{
$this->initObserversGroup($name);
$group = $this->observers[$name];
$all = $this->observers["all"];
return array_merge($group, $all);
}
public function attach(\SplObserver $observer, string $name = "all")
{
$this->initObserversGroup($name);
$this->observers[$name][] = $observer;
}
public function detach(\SplObserver $observer, string $name = "all")
{
foreach ($this->getObservers($name) as $key => $o) {
if ($o === $observer) {
unset($this->observers[$name][$key]);
}
}
}
public function notify(string $name = "all", $data = null)
{
foreach ($this->getObservers($name) as $observer) {
$observer->update($this, $name, $data);
}
}
}
Next, we use the trait in our SplSubject User class:
class User implements \SplSubject
{
// It's necessary to alias construct() because it
// would conflict with other methods.
use SubjectTrait {
SubjectTrait::construct as protected constructSubject;
}
public function __construct()
{
$this->constructSubject();
}
public function create()
{
// User creation code...
$this->notify("User:created");
}
public function update()
{
// User update code...
$this->notify("User:updated");
}
public function delete()
{
// User deletion code...
$this->notify("User:deleted");
}
}
The last step is to implement a reusable SplObserver. This observer is able to bind himself to a Closure (anonymous function).
class MyObserver implements SplObserver
{
protected $closure;
public function __construct(Closure $closure)
{
$this->closure = $closure->bindTo($this, $this);
}
public function update(SplSubject $subject, $name = null, $data = null)
{
$closure = $this->closure;
$closure($subject, $name, $data);
}
}
Now, the test:
$user = new User;
// our custom functions (Closures)
$function1 = function(SplSubject $subject, $name, $data) {
echo $name . ": function1\n"; // we could also use $data here
};
$function2 = function(SplSubject $subject, $name, $data) {
echo $name . ": function2\n";
};
// subscribe the first function to all events
$user->attach(new MyObserver($function1), 'all');
// subscribe the second function to user creations only
$user->attach(new MyObserver($function2), 'User:created');
// run a couple of methods to see what happens
$user->create();
$user->update();
The output will be:
User:created: function2
User:created: function1
User:updated: function1
NOTE: we could use SplObjectStorage instead of an array, to store observers in the trait.
I have a slightly different problem where I'm instantiating a class from within another class which works when I manually invoke the method but doesn't when I automate it. The code is below:
class Button {
function __construct($params = Array()) {
// get some common properties
}
}
class HelperButton extends Button {
function __construct($params = Array()) {
parent::__construct($params);
}
function getHelperButton() {
//generate helper button code
}
}
I'm using the above class in the following classes
include_once('field.class.php');
include_once('buttons.class.php');
class Field {
function __construct() {
}
function setName($name){
$this->name = $name;
}
}
class TextField extends Field {
function __construct() {
parent::__construct();
}
function setFiller($fill = "") {
$helperbtn = new HelperButton($fill);
$this->helperbtn = $helperbtn->getHelperButton();
}
function getTextField(){
$this->textfield = "<input name='blah' />".$this->heperbtn;
return $this->textfield;
}
}
class Segment extends Field {
function __construct() {
parent::__construct();
}
function addTextField($params = array()) {
if(is_array($params)){
$txtfld = new TextField();
}
if (isset($params['type']['filler'])) {
$txtfld->setFiller($params['type']['fill']);
}
$this->segment .= $txtfld->getTextField();
}
function addFillerField($params = array()) {
$params['type']['filler'] = true;
$this->addTextField($params);
}
}
Now, I'm generating the text field in my page dynamically with params. The following code works well:
$segment = new Segment();
$segment->addFillerField("type"=>array("filler"=>true,"fill"=>"sometext")); //this should add a button next to the field which will open a window that will load the 'fill' text
$segment->render(); // this will print the segment output to screen
However, the following code does not work. It prints the text field alright but it does not print the button:
$fldmthds = array("FLRFLD" => "FillerField");
$pagedtl = array("FCLSID" => "FLRFLD", "FLDNAM" => "Field Name", "FLDTYP" => "filler:true,fill:sometext");
$mysegment = new Segment();
foreach ($pagedtl as $flds => $val) {
$mthd = "add" . $fldmthds[$val['FCLSID']]; // resolves to addFillerField
$params = array(
'name' => $val['FLDNAM'],
'type' => json_decode("{" . $val['FLDTYP'] . "}", true)
);
$mysegment->{$mthd}($params);
}
$mysegment->render();
PHP did not give any error/warning. It just goes silent and forgets the button. Does anyone see what is going wrong here? Thanks for your time.
Can't comment for now... just one thing: json_decode() will fail. To receive valid json change {filler:true,fill:sometext} to {"filler": true, "fill": "sometext"}
Im busy with an image moving function so im overriding some controller functions, and unfortuantly i required the items id for the image name so i changed form save() to postSaveHook() as i was not able to get the item id in save() but now im facing another problem i cant set form data to the newly renamed image.
Here's the code:
public function postSaveHook($model, $validData){
$item = $model->getItem();
$id = $item->get('id');
$path = JPath::clean(JPATH_SITE. DS ."images". DS ."menu_slider". DS );
$input=JFactory::getApplication()->input;
$input->get('jform', NULL, NULL);
$src_image = $this->moveOriginal($path,$id);
$imageTest = $this->findImages($src_image);
if(!empty($imageTest)){
foreach($imageTest as $images){
$this->createImageSlices($images,$src_image,$path);
}
}else{
echo 'all images are there';
}
/*this part no longer works*/
$data = JRequest::getVar( 'jform', null, 'post', 'array' );
$data['image'] = 'images'.DS.'menu_slider'.DS.'original'.DS.$src_image;
$input->post->set('jform',$data);
return parent::postSaveHook($model, $validData);
}
is there anyway i can save the data from this? or if i revert back to save, how would i get the id?
Any Help Greatly Appreciated.
I tried different ways and the absolute safest way for me was to add the following code in the model
class DPCasesModelCase extends JModelAdmin {
public function save($data) {
new EventHandler(JDispatcher::getInstance(), $this);
return parent::save($data);
}
}
class EventHandler extends JEvent {
private $model = null;
public function __construct(&$subject, $model) {
parent::__construct($subject);
$this->model = $model;
}
public function onContentChangeState($context, $pks, $value) {
if ($context != 'com_dpcases.case' && $context != 'com_dpcases.form') {
return;
}
if (! is_array($pks)) {
$pks = array($pks);
}
foreach ( $pks as $pk ) {
$this->dowork($this->model->getItem($pk), 'edit');
}
}
public function onContentAfterSave($context, $object, $isNew) {
if ($context != 'com_dpcases.case' && $context != 'com_dpcases.form') {
return;
}
$this->dowork($object, $isNew ? 'create' : 'edit');
}
private function dowork($object, $action) {
...
}
}
Here is my code:
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->_forward('search');
}
public function searchAction()
{
if($this->getRequest()->isGet())
{
$form = $this->getForm();
$value = $form->getValue('search');
echo "'", $value, "'"; // <-- prints ''
}
$this->view->form = $this->getForm();
}
public function getForm()
{
$form = new Zend_Form();
$form->setAction('/index/search')
->setMethod('get');
$search = $form->createElement('text', 'search');
$search->addFilter('StripTags')
->addFilter('StringTrim');
// Add elements to form:
$form->addElement($search)
->addElement('submit', 'submit', array('label' => 'Search'));
return $form;
}
}
In searchAction() $value is always blank even after I submit the form and I see the data in the URL. What is the problem?
EDIT: I fixed getValue() to getValues() but it still isn't working.
Before you are able to use ->getValue(); you have to validate the form.
public function searchAction()
{
if($this->getRequest()->isGet())
{
$form = $this->getForm();
if ($form->isValid()) {
$value = $form->getValue('search');
echo "'", $value, "'"; // <-- prints ''
} else {
// what ever
}
}
$this->view->form = $this->getForm();
}
You need to pass $this->_request->getParams() to $form->isValid(), otherwise the form will not have any values to work with.
The function you want is getValue, not getValues.
A subtle difference but they do two totally different things.
Are there any libraries to protect against CSRF(PHP5.1/5.2) or do I need to create on myself? I use this snippet from Chris, but without a library I am getting a lot of duplication on every page.
I found this library for PHP5.3, but I am wondering if there are any on PHP5.1/5.2 because I don't believe yet all hosting support PHP5.3.
Since I use Kohana - I've just extended couple of its core classes. It can be used in any code with a little changes though:
class Form extends Kohana_Form
{
public static function open($action = NULL, array $attributes = null)
{
if (is_null($action))
{
$action = Request::current()->uri . ($_SERVER['QUERY_STRING'] ? '?' . $_SERVER['QUERY_STRING'] : '');
}
$open = parent::open($action, $attributes);
$open .= parent::hidden(self::csrf_token_field(), self::csrf_token());
return $open;
}
public static function csrf_token_field()
{
return 'csrf_token';
}
public static function csrf_token()
{
$session = Session::instance();
$token = $session->get(self::csrf_token_field());
if (!$token)
{
$session->set(self::csrf_token_field(), $token = md5(uniqid()));
}
return $token;
}
}
class Validate extends Kohana_Validate
{
public function __construct(array $array, $csrf = true)
{
parent::__construct($array);
if ($csrf)
$this->add_csrf();
}
public static function factory(array $array, $csrf = true)
{
return new Validate($array, $csrf);
}
private function add_csrf()
{
$this->rules(form::csrf_token_field(), array(
'not_empty' => array(),
'csrf' => array()
));
}
protected function csrf($token)
{
return $token == form::csrf_token();
}
}