picocms / Pico

Pico is a stupidly simple, blazing fast, flat file CMS.
http://picocms.org/
MIT License
3.81k stars 616 forks source link

php mail, avoid form refresh page #632

Closed digitalinferno closed 2 years ago

digitalinferno commented 2 years ago

I have a form, my plugin read the $_POST var and give back the result to $twigVariables. All seem to work, but I want to avoid the page reload.

index.twig

 <form role="form" id="contact-form" method="post" data-toggle="validator">
...
...
</form>
{% if msg %}
<div class="alert alert-{{ error == false ? "success" : "danger" }} > {{ msg }} </div>
{% endif %} 

plugin.php (onPageRendering)

if($_SERVER["REQUEST_METHOD"] == "POST"){
...
...
$twigVariables['error'] = $error; // false or true
$twigVariables['msg'] = $msg; // success or fail message
}

Any hint to solve the issue?

mayamcdougall commented 2 years ago

To my knowledge, reloading the page is just how POST works.

The only way around this is to use Javascript to send the POST data instead.

However, if you do this, you won't get that data back into Pico or Twig, since all the execution is done server-side before the page is rendered. You'd have to also use Javascript to handle your success/error message as well.

Assuming you're a bit more technically inclined, check out what I did for my Freelancer port.

The original Start Bootstrap contact form used a proprietary server-side script for validation (to upsell their premium service), so I had to replace it if I wanted validation to work in my version.

I wrote this Javascript file to provide very basic browser-based form validation (something built-in to the browser, but requiring a script to utilize), and to send the POST data via an Ajax request without reloading the page. 😁

I have to warn you though that I am NOT a Javascript developer, and there might be some problems with my implementation. I cobbled it together from a few different tutorials, using the pieces I specifically needed.

This contact form also activates several styles provided by the original theme and its underlying Bootstrap base to highlight any errors the browser-based validation detects when you try to send the form.

I can't say it wouldn't be a bit of a research project, but using Javascript and a client-side script is as far as I know the ONLY way to have a form that sends without reloading. Trust me, I looked for any excuse I could find to not have to dive into JS for it. 😂

So, hopefully this will push you in the right direction. Wish I could be more help on this, but I barely managed it myself when I did. I even have a big disclaimer in the readme that my Javascript could be a complete mess and probably shouldn't be trusted! 😅

Let me know if you have any other questions though and I'll be happy to help where I can. 😉

digitalinferno commented 2 years ago

Ok, I have already a similar JS to make the magic happen (it works with a separate mail.php). But I want to make a plugin, so I build a fake mail.php and read its output with the js:

class PHPmail extends AbstractPicoPlugin
{
    public function onRequestUrl(&$url)
    {
        // If url/mail.php, then true
        if ($url == 'mail.php') {
            $this->phpMail = true;
        }
    }

    public function onPagesLoaded()
    if ($this->phpMail  and  _POST){
            ...
            $output = 'success';
            die ($output);
    }
}

If I point to localhost/mail.php this give me the same output as if there was the php file on the server, so it seems to work.

But ajax fails

    $('#contact-form').on('submit',function(){

    // Add text right after clicking 
    $('.alert').text('Loading...');

    var form = $(this);   
    $.ajax({
        method: "POST",
        url: "mail.php",
    ...

There is definitely something I do not understand ... The js does not give me positive or negative results, but it remains stuck on Loading..., I probably cannot request the fake mail.php from the js or the onPagesLoaded is wrong?

digitalinferno commented 2 years ago

Self reply. Just add a header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK'); and JS works.

mayamcdougall commented 2 years ago

Sorry, been away for a few days and I need to catch up on things. 😅

Glad you got it worked out.

As I said before, I'm WAY out of my league when dealing with Javascript, PHP, or POST requests, so I'm not sure I could have helped much anyway. 😅