<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chuck Watson</title>
    <description>The latest articles on DEV Community by Chuck Watson (@madeinmilwaukee).</description>
    <link>https://dev.to/madeinmilwaukee</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F272152%2Fb68f616d-4cb5-4903-98cb-c6234c3b5b92.jpeg</url>
      <title>DEV Community: Chuck Watson</title>
      <link>https://dev.to/madeinmilwaukee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/madeinmilwaukee"/>
    <language>en</language>
    <item>
      <title>Contact Form with Recapcha v3 and PHP</title>
      <dc:creator>Chuck Watson</dc:creator>
      <pubDate>Wed, 11 May 2022 15:07:10 +0000</pubDate>
      <link>https://dev.to/madeinmilwaukee/email-form-with-recapcha-v3-kb0</link>
      <guid>https://dev.to/madeinmilwaukee/email-form-with-recapcha-v3-kb0</guid>
      <description>&lt;p&gt;I recently decided to update the old contact form on my website as it was an old wordpress plugin with a fairly non-functional verification code system. I figured I would DIY and use &lt;a href="https://developers.google.com/recaptcha/docs/v3"&gt;Google's Recapcha V3&lt;/a&gt;. It is basic html, vanilla javascript and PHP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;the HTML&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div id="contact-form-box"&amp;gt;
    &amp;lt;form method="post" id="contactform" action="your-file-to-post-to.php"&amp;gt;
        &amp;lt;div&amp;gt;
            &amp;lt;label for="name"&amp;gt;Name &amp;lt;span class="req"&amp;gt;*&amp;lt;/span&amp;gt;&amp;lt;/label&amp;gt;
            &amp;lt;input id="name" type="text" class="einput" name="name" required="required"&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;
            &amp;lt;label for="email"&amp;gt;Email &amp;lt;span class="req"&amp;gt;*&amp;lt;/span&amp;gt;&amp;lt;/label&amp;gt;
            &amp;lt;input id="email" type="email" class="einput" name="email" required="required"&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;
            &amp;lt;label for="message"&amp;gt;Message &amp;lt;span class="req"&amp;gt;*&amp;lt;/span&amp;gt;&amp;lt;/label&amp;gt;
            &amp;lt;textarea id="message" class="einput" name="message" required="required"&amp;gt;&amp;lt;/textarea&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div id="errorlog"&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;
            &amp;lt;button type="submit"&amp;gt;Submit&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;the javascript&lt;/strong&gt;&lt;br&gt;
We get the recapcha token and submit the form. Any errors thrown put a user friendly message in the form's #errorlog div.&lt;br&gt;
There are 2 functions, one to deal with any errors thrown, and one to submit the form. First I set it so the submit button is &lt;a href="https://www.w3schools.com/jsref/prop_pushbutton_disabled.asp"&gt;disabled&lt;/a&gt; while the form is submitting.  Then I check for valid fields &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLSelectElement/checkValidity"&gt;checkValidity()&lt;/a&gt;, to avoid any incomplete submissions. Finally, I use the Google provided script to get a recapcha token and submit the form using javascript's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch"&gt;fetch api&lt;/a&gt;. If there are no errors the form is cleared out and a submitted message is put inside of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="https://www.google.com/recaptcha/api.js?render=YOUR RECAPCHA CODE"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;

function doError(msg)
{
    button.disabled = false;
    button.textContent = "Submit";
    log.className = "error message";
    log.textContent = msg;
}

function formSubmit(event)
{
    event.preventDefault();
    button.disabled = true;
    button.textContent = "Sending...";
    log.className = "";
    log.textContent = "";
    if(!form.checkValidity()){
        doError("Please fill out all fields.");
    }
    else{
        const formdata = new FormData(form);
        grecaptcha.ready(function() {
            grecaptcha.execute('YOUR RECAPCHA CODE', {action: 'submit'}).then(function(token) {
            formdata.append('g-recaptcha-response', token);
                fetch(form.action, {
                    method: 'POST',
                    body: formdata
                })
                .then((response) =&amp;gt; {
                    if(response.ok){
                        form.innerHTML = "";
                        form.textContent = "Submitted, thanks!";
                        form.classList.add("submitted");
                    }
                    else{
                        response.json().then((data) =&amp;gt; {
                            doError(data.message);
                        })
                    }
                })
                .catch(err =&amp;gt; {
                    doError("Sorry there was a problem submitting the form, please try again.");
                    console.log(err);
                });
            });
        });
    }
}
const form = document.getElementById("contactform");
const log = document.getElementById("errorlog");
const button = form.querySelector("button");
form.addEventListener("submit", formSubmit);

&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;the PHP&lt;/strong&gt;&lt;br&gt;
Finally we have the PHP file (your-file-to-post-to.php) with the  script to deal with the form's posted data.&lt;br&gt;
The checkPostAndSendEmail function does one last check for an empty message, and also checks for recapcha secret, and recapcha token, and validates the email address structure. It then uses &lt;a href="https://www.php.net/manual/en/ref.curl.php"&gt;cURL&lt;/a&gt; to post the secret and token to googles recapcha verify url, checks the response and if no issues it moves on to send the email. My needs are very simple so there's not lots of processing to send the email, just stick the posted key value pairs together with &lt;a href="https://www.php.net/manual/en/function.implode.php"&gt;implode()&lt;/a&gt;, array_map, array_keys, sprintf, and send it out. The end result is a json string sent back to the javascript with status (200 or 400) and a message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function checkPostAndSendEmail($post=[],$secret=''){
  if(empty($secret)){
    throw new \Exception('fail');
  }
  if(empty($post['g-recaptcha-response'])){
    throw new \Exception('Recapcha empty');
  }
  if (!filter_var($post['email'], FILTER_VALIDATE_EMAIL)) {
    throw new \Exception('Bad Email');
  }
  if(empty($post['message'])){
    throw new \Exception('No Message');  
  }
  $recapchapost['secret'] = $secret;
  $recapchapost['response'] = $post['g-recaptcha-response'];
  $url = 'https://www.google.com/recaptcha/api/siteverify';
  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_POSTFIELDS, $recapchapost);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); 
  curl_setopt($ch, CURLOPT_TIMEOUT, 10);
  $response = curl_exec($ch);
  if(curl_errno($ch)){
    throw new \Exception(curl_error($ch));
  }
  curl_close($ch);
  $capcharesponse = json_decode($response,true);
  if(!$capcharesponse['success'] || $capcharesponse['score']&amp;lt;.5){
    throw new \Exception('Recapcha failed '.json_encode($capcharesponse['error-codes']));
  }
  unset($post['g-recaptcha-response']);
  $message = implode('&amp;lt;br&amp;gt;', array_map(function ($v, $k) { return sprintf("%s: %s", $k, $v); }, $post, array_keys($post)));
  $to = 'EMAIL';
  $subject = 'SUBJECT';
  $headers = "MIME-Version: 1.0\r\n";
  $headers .= "Content-type: text/html; charset=iso-8859-1\r\n";
  $headers .= "X-Priority: 3\r\n";
  $headers .= "X-Mailer: PHP". phpversion() ."\r\n";

  if(!mail($to, $subject, $message, $headers)){
    throw new \Exception('Unable to send email');
  }
}

if(isset($_POST['name'])){
  try{
    checkPostAndSendEmail($_POST,'RECAPCHA SECRET');
    http_response_code(200);
    $response_message = 'message sent';
  }
  catch(\Exception $e){
    http_response_code(400);
    $response_message = $e-&amp;gt;getMessage();
  }
}
else{
  http_response_code(400);
  $response_message = 'sorry, fail.';
}
header('Content-type: application/json');
exit(json_encode(['message'=&amp;gt;$response_message]));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about the Recapcha process and details on &lt;a href="https://developers.google.com/recaptcha/docs/v3"&gt;Google's site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>recapcha</category>
      <category>php</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Shopify Recommended Products AJAX API and Sections</title>
      <dc:creator>Chuck Watson</dc:creator>
      <pubDate>Tue, 10 Aug 2021 15:44:06 +0000</pubDate>
      <link>https://dev.to/madeinmilwaukee/shopify-recommended-products-ajax-api-and-sections-2c23</link>
      <guid>https://dev.to/madeinmilwaukee/shopify-recommended-products-ajax-api-and-sections-2c23</guid>
      <description>&lt;p&gt;Throwing this up here in hopes it is easier to find than some of the random examples I found after lots of looking.&lt;br&gt;
Anyway, Shopify's AJAX unauthenticated API is pretty limited in scope but its &lt;a href="https://shopify.dev/api/ajax/reference/product-recommendations"&gt;recommended products&lt;/a&gt;  suggestions are certainly more robust than whatever clever looping and coding that I can do within a liquid template.&lt;br&gt;
Also in the one fairly thorough example I found they don't completely explain how sections work with this. So i'll try to do that here.&lt;br&gt;
When you create a url to fetch recommendations from you can add 3 variables: the id of the product (required), the limit of items to pull, and the section id that will render the results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var productId="12345"; //{ in a template it would be product.id }
var sectionId="product-recommendations";
var limit="4";
var url="/recommendations/products?section_id="+sectionId+"&amp;amp;limit="+limit+"&amp;amp;product_id="+productId;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you only do the product id you will get up to 10 product results in json format. And if javascript only is your thing then you're all set. For me having a simple liquid section to format the data is easier. So in our section named "product-recommendations" we have some liquid and HTML. The recommendations object (recommendations.products) is a set of products and we will loop through it to show all the items. Of course the result could be empty so we will check for that too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="product-recommendations"&amp;gt;
  {%- if recommendations.products_count &amp;gt; 0 -%}
    &amp;lt;h2&amp;gt;You may also like&amp;lt;/h2&amp;gt;
      {%- for product in recommendations.products -%}
        &amp;lt;div&amp;gt;Item url: {{ product.url }}&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;Image url: {{ product.featured_image | img_url: '300x300' }}"&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;Item name: {{ product.title }}&amp;lt;/div&amp;gt;
        &amp;lt;div&amp;gt;Item price: {{ product.price | money}}&amp;lt;/div&amp;gt;
      {%- endfor -%}
  {%- endif -%}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty much anything in the normal &lt;a href="https://shopify.dev/api/liquid/objects/product"&gt;product object&lt;/a&gt; is available so mark it up however you need for your shop. So using javascript fetch and assuming you have a container div on your page with the id "product-recs" you would have this (using the url we built earlier)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fetch(url).then(function (response) {
    return response.text();
}).then(function (html) {
    document.getElementById("product-recs").innerHTML=html;
}).catch(function (err) {
    console.warn('Something went wrong.', err);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that is pretty much it! You could add this to any product page, or the cart page (use the product id of the first or last item in the cart), or even the accounts area (maybe get the customer favorite item id and use that etc).&lt;br&gt;
BUT, there is one more thing i'll add here, and that is a way to save some initial load time on the pages you implement this. As our code stands it will load the items as part of the page load, but most usually recommendations are found at the bottom of the page below the fold, and out of sight. So lets load them when they enter the viewport. For this we will use javascript's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API"&gt;IntersectionObserver&lt;/a&gt;. A very nice and clean way to trigger our fetch (rather than listening to scroll events).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var productId="12345"; //{ in a template it would be product.id }
var sectionId="product-recommendations";
var limit="4";
var url="/recommendations/products?section_id="+sectionId+"&amp;amp;limit="+limit+"&amp;amp;product_id="+productId;
var intersectionObserver = new IntersectionObserver(function(entries) {
  // If intersectionRatio is 0, the target is out of view
  // and we do not need to do anything.
  if (entries[0].intersectionRatio &amp;lt;= 0) return;
  fetch(url).then(function (response) {
//once loaded we can stop observing
intersectionObserver.unobserve(document.getElementById("product-recs"));
return response.text();
}).then(function (html) {
    document.getElementById("product-recs").innerHTML=html;
}).catch(function (err) {
    console.warn('Something went wrong.', err);
});
    console.log('Loaded new items');
});
// start observing
intersectionObserver.observe(document.getElementById("product-recs"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last thing: assuming you make a section named "product-recommendations" with the HTML markup you need, here's how to add this to your theme.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Add a div with the id "product-recommendations" 
where you want suggestions to show up 
in your product.liquid template.
Add this script to your theme footer
{% if request.page_type == "product" %}
&amp;lt;script&amp;gt;
if(document.getElementById("product-recommendations")){
      var intersectionObserver = new IntersectionObserver(function(entries) {
  if (entries[0].intersectionRatio &amp;lt;= 0) return;
  var requestUrl = "/recommendations/products?section_id=product-recommendations&amp;amp;limit=4&amp;amp;product_id="+{{ product.id }};
  fetch(requestUrl).then(function (response) {
intersectionObserver.unobserve(document.getElementById("product-recommendations"));
    return response.text();
}).then(function (html) {
        document.getElementById("product-recommendations").innerHTML=html;
}).catch(function (err) {
    console.warn('Something went wrong.', err);
});
});
intersectionObserver.observe(document.getElementById("product-recommendationss"));  
}
&amp;lt;/script&amp;gt;
{% endif %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm using this on a project and it is working well. Hope this is helpful, happy coding!&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>javascript</category>
      <category>intersectionobserver</category>
      <category>liquid</category>
    </item>
    <item>
      <title>PHP, MYSQLI, and CSV Export from database</title>
      <dc:creator>Chuck Watson</dc:creator>
      <pubDate>Thu, 13 Feb 2020 15:24:31 +0000</pubDate>
      <link>https://dev.to/madeinmilwaukee/php-mysqli-and-csv-export-from-database-5a3j</link>
      <guid>https://dev.to/madeinmilwaukee/php-mysqli-and-csv-export-from-database-5a3j</guid>
      <description>&lt;p&gt;First contribution here and thought I'd share a snippet from a recent project. When scrubbing around the net for info on how to output a csv file directly to the browser I noticed there are quite a few outdated and overly verbose examples.  Here's what I ended up with.&lt;/p&gt;

&lt;p&gt;My project needed the data to be pulled by date so this example uses a date field. For me, a user is picking a date and submitting a form to get the csv download so I am using a prepared statement to avoid any mysql injection.&lt;br&gt;
I didn't put in error handling for brevity's sake, but you can mark up your code with as much or little as you see fit.&lt;/p&gt;

&lt;p&gt;The examples I have seen usually use a bunch of concatenation when iterating over the data to separate out and build the header line and the body lines but there are a few biult in functions that replace all that &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.php.net/manual/en/function.fputcsv.php"&gt;fputcsv&lt;/a&gt; - formats array line to csv format&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.php.net/manual/en/function.array-values.php"&gt;array_values&lt;/a&gt; - grab the values of associative array and add them to the csv array&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.php.net/manual/en/function.array-keys.php"&gt;array_keys&lt;/a&gt; - set the header of the csv file if it is needed using the returned associative array&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is working for me currently, and producing expected results. I'm not an expert by any means but I hope this is helpful to someone out there looking for examples on how to do this.&lt;/p&gt;

&lt;p&gt;This example should work for PHP 5 &amp;gt;= 5.3.0, PHP 7.&lt;/p&gt;

&lt;pre&gt;
$conn = new mysqli('myHOST', 'myUSER', 'myPASS', 'myDB');
$date=date('Y-m-d'); //this would be $_POST['date'] from the form
$q = "SELECT dateColumn,column2,column3,column4 FROM myTABLE WHERE dateColumn = ? ";
$stmt = $conn-&amp;gt;prepare($q);
$stmt-&amp;gt;bind_param('s', $date);
$stmt-&amp;gt;execute();
$result = $stmt-&amp;gt;get_result();
if ($result-&amp;gt;num_rows &amp;gt; 0) {
   //These next 3 Lines Set the CSV header line if needed
   $data = $result-&amp;gt;fetch_assoc();
   $csv[] = array_keys($data);
   $result-&amp;gt;data_seek(0);
   //SET THE CSV BODY LINES
    while ($data = $result-&amp;gt;fetch_assoc()) {
        $csv[] = array_values($data);
    }
    header('Content-Type: application/csv');
    header('Content-Disposition: attachment; filename="myCSV.csv";');
    //You could save the file on your server
    //but we want to download it directly so we use php://output
    $f = fopen('php://output', 'w');
    foreach ($csv as $line) {
        fputcsv($f, $line, ',');
    }
    exit;
} else {
    echo 'No data to export for ' . $date;
}
&lt;/pre&gt;

</description>
      <category>php</category>
      <category>csv</category>
      <category>mysqli</category>
    </item>
  </channel>
</rss>
