Category Archives: Software Testing & Debugging

Don’t Hard Code WP-Content Directory!

Plugin development tip of the day!
DO NOT HARD CODE wp-content Directory!

I made this mistake not realizing how many people change that directory as a security precaution.

Alternatives

The best alternative is probably the WP_CONTENT_DIR in my opinion. You can also use WP_CONTENT_URL, depending if you want a relative path or a full url.

Examples:
Here is an example of each followed by the output code:
<?php
echo WP_CONTENT_DIR;
echo "<br />";
echo WP_CONTENT_URL;
echo "<br />";
?>

/var/www/html/jafty.com/public_html/wp-content
http://jafty.com/wp-content

So now you know!

Want more? Here are some other ways to get similar paths or directories  from within a plugin file:

<?php
echo "<br>";
echo plugins_url( 'myscript.js', __FILE__ );
echo "<br>";
echo plugins_url();
echo "<br>";
echo plugin_dir_url(__FILE__) ;
echo "<br>";
echo plugin_dir_path(__FILE__) ;
echo "<br>";
echo plugin_basename(__FILE__) ;
?>

The above would output the following lines if it were ran from a plugin on jafty.com:

http://jafty.com/wp-content/plugins/jafty_plugin_tester/test_code/myscript.js

http://jafty.com/wp-content/plugins

http://jafty.com/wp-content/plugins/jafty_plugin_tester/test_code

//var/www/html/jafty.com/public_html/wp-content/plugins/jafty_plugin_tester/test_code/

jafty_plugin_tester/test_code/wp_content_url.php

EDD Instant Updater WordPress Plugin

The EDD Instant Updater WordPress plugin is a plugin I created while working with a custom WordPress plugin that needed to have instant update notifications while using Easy Digital Downloads(EDD). EDD comes with it's own updater class, but it uses the default WP standards which means you have to wait up to 12 hours after you push an update to get the notice in the WordPress dashboard! With the EDD Instant Updater plugin, you no longer have to wait, the plugin update notices come to you instantly! No more delays!

Interested? Contact Ian L of Jafty.com by email at linian11@yahoo.com for more information.

How to Fix Max File Upload Size on Godaddy and Other Shared hosting Accounts

I ran into this problem using WordPress today where I reached the max upload size on my client's Godaddy server on a shared hosting account. If you get this error in WordPress, while uploading a file, by the way, it's probably a max file size issue:

Error: 1

That's all it said! So it took me a few guesses to figure out what that "Error: 1" message meant.

How to increase the Max File Size Setting in Cpanel

1 - go to your site's Cpanel by going to yoursite.com/cpanel and log in with the user and password your hosting provider provides for you. You should see this when logged in:

cpanel1

2) scroll down until you see the "Select PHP Version" link in Cpanel that is circled in the below image and click on it:

cpanel1b

3) That will take you to the PHP version selection page. Then click the link in the upper right corner of the page that you see circled in the below image:

cpanel2

4) Clicking the "Switch to PHP Options" link in the above image will take you to a PHP settings page. Look for the upload_max_filesize setting that you see circled in the image below and set it to something higher than 2M according to your individual needs:

cpanel3b

5) After you change it from "2M" to something higher as needed, click the "Apply" button directly to the right of the dropdown and then be sure to also click the "Save" button near the bottom-left of the page as well and then you are done!

WordPress Plugin Admin Page Unwanted Scroll

I was working on a custom plugin for an important client today and one of the plugin's admin pages would scroll half way down the page every time I landed on it and I couldn't figure out why at first!

Long story short, I found out what was causing it was an inline JavaScript block of code, so If anyone has this issue in a WordPress admin page while making a plugin, try getting rid of the JavaScript and it should stop the page from scrolling as it did in my case.

If you experience something similar, please mention how you solved it in the comments.

MailChimp API

I've been doing a lot of work with the MailChimp API lately and figured it would be good to have a page dedicated to it here for future reference because it's not very well documented online anywhere else... Hoping this will help some people who also have to use MailChimp's API.

Understanding MailChimp Lists and the API

If you've ever dealt with MailChimp, you know that it is based on lists which can be segmented, grouped and managed all from the API as needed. Next I will demonstrate how to get a list of MailChimp lists and information on each list that exists.

Getting MailChimp List Information from API

Here's the code I created to get all MailChimp Lists and information on each list from the MailChimp API using PHP code:

<h2>MailChimp List Info:</h2>
<?php
//Get API key :

$mcAPIkey ='Enter_your_own_API_key_HERE';
//get info regarding MC lists
//use following url 'https://usX.api.mailchimp.com/3.0/lists'

$dc = substr( $mcAPIkey, strpos( $mcAPIkey, '-' ) + 1 ); // datacenter, it is the part of your api key - us5, us8 etc
$args = array(
     'headers' => array(
        'Authorization' => 'Basic ' . base64_encode( 'user:'. $mcAPIkey )
    )
);

$response = wp_remote_get( 'https://'.$dc.'.api.mailchimp.com/3.0/lists/', $args );
$body = json_decode(wp_remote_retrieve_body($response));

$rcode = wp_remote_retrieve_response_code($response);
$tot_itms = $body->total_items;
echo "Response code was $rcode ...$tot_itms items found!<br /><hr />";
echo "<pre><code>";
print_r($body);
echo "</code></pre><hr />";
?>

The above code would return something like this for a MailChimp account that has one saved list:

MailChimp List Info:

Response code was 200 ...1 items found!


stdClass Object
(
    [lists] => Array
        (
            [0] => stdClass Object
                (
                    [id] => xxxxxxxxx
                    [web_id] => xxxxxx
                    [name] => List Name
                    [contact] => stdClass Object
                        (
                            [company] => Company Name
                            [address1] => 1001 Your Road
                            [address2] => 
                            [city] => Adolphus
                            [state] => KY
                            [zip] => 42120
                            [country] => US
                            [phone] => 
                        )

                    [permission_reminder] => You signed up to learn more about how our team can better serve you.
                    [use_archive_bar] => 1
                    [campaign_defaults] => stdClass Object
                        (
                            [from_name] => First Last
                            [from_email] => your_email@domain.com
                            [subject] => 
                            [language] => en
                        )

                    [notify_on_subscribe] => 
                    [notify_on_unsubscribe] => 
                    [date_created] => 2018-02-25T02:01:38+00:00
                    [list_rating] => 0
                    [email_type_option] => 
                    [subscribe_url_short] => http://eepurl.com/dBXF4r
                    [subscribe_url_long] => https://yourcompany.us13.list-manage.com/subscribe?u=bf405c1xxxxxxxxb48df64c3&id=xxxxxxxxxx
                    [beamer_address] => us13-5df785xxxxx-xxx@inbound.mailchimp.com
                    [visibility] => pub
                    [double_optin] => 1
                    [marketing_permissions] => 
                    [modules] => Array
                        (
                        )

                    [stats] => stdClass Object
                        (
                            [member_count] => 3
                            [unsubscribe_count] => 0
                            [cleaned_count] => 0
                            [member_count_since_send] => 5
                            [unsubscribe_count_since_send] => 0
                            [cleaned_count_since_send] => 0
                            [campaign_count] => 2
                            [campaign_last_sent] => 
                            [merge_field_count] => 4
                            [avg_sub_rate] => 1
                            [avg_unsub_rate] => 0
                            [target_sub_rate] => 0
                            [open_rate] => 0
                            [click_rate] => 0
                            [last_sub_date] => 2018-04-28T20:26:38+00:00
                            [last_unsub_date] => 
                        )

                    [_links] => Array
                        (
                            [0] => stdClass Object
                                (
                                    [rel] => self
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/edxxxxxxxxx
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Response.json
                                )

                            [1] => stdClass Object
                                (
                                    [rel] => parent
                                    [href] => https://us13.api.mailchimp.com/3.0/lists
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists.json
                                )

                            [2] => stdClass Object
                                (
                                    [rel] => update
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/edxxxxxxxxx
                                    [method] => PATCH
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Response.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/PATCH.json
                                )

                            [3] => stdClass Object
                                (
                                    [rel] => batch-sub-unsub-members
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxxx
                                    [method] => POST
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/BatchPOST-Response.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/BatchPOST.json
                                )

                            [4] => stdClass Object
                                (
                                    [rel] => delete
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxxxxx
                                    [method] => DELETE
                                )

                            [5] => stdClass Object
                                (
                                    [rel] => abuse-reports
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxx/abuse-reports
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Abuse/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists/Abuse.json
                                )

                            [6] => stdClass Object
                                (
                                    [rel] => activity
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxx/activity
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Activity/Response.json
                                )

                            [7] => stdClass Object
                                (
                                    [rel] => clients
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxxxxxx/clients
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Clients/Response.json
                                )

                            [8] => stdClass Object
                                (
                                    [rel] => growth-history
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxxxxxxxxx/growth-history
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Growth/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists/Growth.json
                                )

                            [9] => stdClass Object
                                (
                                    [rel] => interest-categories
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxxx/interest-categories
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/InterestCategories/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists/InterestCategories.json
                                )

                            [10] => stdClass Object
                                (
                                    [rel] => members
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxx/members
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Members/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists/Members.json
                                )

                            [11] => stdClass Object
                                (
                                    [rel] => merge-fields
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxxx/merge-fields
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/MergeFields/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists/MergeFields.json
                                )

                            [12] => stdClass Object
                                (
                                    [rel] => segments
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxxxxx/segments
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Segments/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists/Segments.json
                                )

                            [13] => stdClass Object
                                (
                                    [rel] => webhooks
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxx/webhooks
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Webhooks/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists/Webhooks.json
                                )

                            [14] => stdClass Object
                                (
                                    [rel] => signup-forms
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxxxxx/signup-forms
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/SignupForms/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists/SignupForms.json
                                )

                            [15] => stdClass Object
                                (
                                    [rel] => locations
                                    [href] => https://us13.api.mailchimp.com/3.0/lists/xxxxxxxxxxx/locations
                                    [method] => GET
                                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Locations/CollectionResponse.json
                                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists/Locations.json
                                )

                        )

                )

        )

    [total_items] => 1
    [_links] => Array
        (
            [0] => stdClass Object
                (
                    [rel] => self
                    [href] => https://us13.api.mailchimp.com/3.0/lists
                    [method] => GET
                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/CollectionResponse.json
                    [schema] => https://us13.api.mailchimp.com/schema/3.0/CollectionLinks/Lists.json
                )

            [1] => stdClass Object
                (
                    [rel] => parent
                    [href] => https://us13.api.mailchimp.com/3.0/
                    [method] => GET
                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Root/Response.json
                )

            [2] => stdClass Object
                (
                    [rel] => create
                    [href] => https://us13.api.mailchimp.com/3.0/lists
                    [method] => POST
                    [targetSchema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/Response.json
                    [schema] => https://us13.api.mailchimp.com/schema/3.0/Definitions/Lists/POST.json
                )

        )

)

How to Count Iterations of a PHP Function

I find myself having to do this all the time! I need to know how many times a function is running and for some strange reason, I almost never remember how to do it correctly. That's why I am posting this simple trick on my blog, so others can find it easily and so I can look at how I did it next time I need to do it again!

Here's a simple example to illustrate the need here:

<?php
function runMe(){

$i = 0;

$i++;

echo "$i<br>";

}

//If you were to execute the above function in a loop like this:

for($x=0;$x<10;$x++){

runMe();

}
?>

The output of the above would be:

1
1
1
1
1
1
1
1
1
1

Not what you wanted? or expected? Often we need it to output something like this instead:

1
2
3
4
5
6
7
8
9...

Static Variable to the Rescue

In comes what is know in PHP as Static Variables.  Here's a quick code example that should make you understand how they work:

<?php
function countRuns(){

static $c = 0;

$c++;

echo "$c<br />";

}

//Then, Executing the countRuns function in a for loop will increment the number printed ea. time:

for($x=0;$x<10;$x++){

countRuns();

}
?>

If you were to run the above tested code in your browser you would get the following output:

1
2
3
4
5
6
7
8
9
10

That's what I was looking for! okay, so that's how it's done. I hope this helps someone else besides me.

 

 

Getting Unterminated String Literal Error When Using Newlines In JavaScript Variable

Nothing like a good Unterminated String Literal Error to fog up your day huh? Well I found a neat little trick to squash this nasty bug with JavaScript today!

The Problem

The problem in my case is that I wanted to place the contents of a PHP variable, filled with data from a database table, into a JavaScript variable which I thought could be easily done like in  the following JavaScript code example:

var sc = "<?php echo $email_content; ?>";

WRONG! I discovered that when the PHP variable holds text with newline characters in it, like you'll often encounter when retrieving data from a database or from a textarea, or even a file, JavaScript throws the nasty old "Unterminated string literal" error! I've learned to hate this general JavaScript error over the years, but today, I stumbled upon a small, yet useful, trick that makes this error go away in cases like this.

The Solution:

Believe it or not, the solution is as simply as replacing quotes in the JavaScript code with backticks. If you don't know what a backtick is, read on, otherwise your JavaScript code should look like this after replacing quotes with backticks:

var sc = `<?php echo $email_content; ?>`;

What is a Backtick?

A backtick is the little character that looks like a slanted single quotation mark that normally calls the key above the tab key home on a normal English keyboard. Otherwise known as the Marigold key, grave accent or backquote, the character is a tricky one simply because no one knows what to call it. However, in the land of computer programming, it is commonly referred to as the backtick character, so that's what we'll call it. Cool? Okay!

 

Sorting Multi-Dimensional Associative Arrays in PHP

Learning  how to sort a multi-dimensional associative array in PHP was a bit difficult for me to figure out, so I hope documenting it here will help others figure it out a little faster.

In many cases, sorting multi-dimensional arrays is used to sort data retrieved from a database. For this exercise, let's say we have the following database structure:

db

The above image is from a database I was working on for a client while building a custom WordPress plugin in PHP. I needed to present the data from the table in order according to two fields, "term" and "qty". The task in my case was extra difficult because it also had to be sorted with a custom function. First we will do a simple sort with built in functions and then I'll explain how I solved my custom sorting issue.

Fetching Data From WordPress Database to Build an Associative Array

I was building a WordPress plugin when I encountered the need for this code, so that is what I'm using as an example, but the code is basically the same if you do it outside of WordPress also. The only difference would be how you do the database query. I am leaving the WordPress code in tact in case some of my readers could benefit from it. We have to build the array first. I'll provide test code below to build an array without having to query the database for those of you who may need it.  Here is the PHP code I wrote to fetch data from a custom table in WordPress

<?php
global $wpdb;
$table_name = $wpdb->prefix . 'table_name';
$where_clause = "WHERE campaign='$t_name'";
$query="SELECT * FROM $table_name $where_clause";
$results = $wpdb->get_results($query);
$rowcount = $wpdb->num_rows;
echo "<p>Rows Found: $rowcount</p>";
if($rowcount > 0){//only continue if there are rows found!
    foreach($results as $r){
        $db_id = $r->id;
        $stp = $r->touchpoint;//saved touchpoint name will be same as $tp
        //get type, qty, term, relative_to and date:
        $typ = $r->type;
        $qty = $r->qty;
        $trm = $r->term;
        $rel_to = $r->relative_to;
        $dat = $r->date;
        //add ea. of the 5 values and an associative array for ordering later on:
        //build an associative multi_dimensional Array of data:
        $assoc_array[$stp]['type'] = $typ;
        $assoc_array[$stp]['qty'] = $qty;
        $assoc_array[$stp]['term'] = $trm;
        $assoc_array[$stp]['order'] = $order;
        $assoc_array[$stp]['relto'] = $rel_to;
        $assoc_array[$stp]['date'] = $dat;
    }//and foreach result
}//end if there were rows of data found
?>

Building a Multi-Dimensional Associative Array in PHP

For those of you not interested in the WordPress part of this exercise, I've also included another way to build the same associative multi-dimensional array in simple PHP code below:

$assoc_array["call owner"] = Array
(
"type" => "Relative",
"qty" => 2,
"term" => "hours",
"order" => "a",
"relto" => "last",
"date" => "none"
);

$assoc_array["email tommorow"] = Array
(
"type" => "Relative",
"qty" => 1,
"term" => "days",
"order" => "b",
"relto" => "last",
"date" => "none"
);

$assoc_array["yearly touchpoint"] = Array
(
"type" => "Relative",
"qty" => 1,
"term" => "years",
"order" => "e",
"relto" => "last",
"date" => "none"
);

$assoc_array["Welcome Email"] = Array
(
"type" => "Relative",
"qty" => 1,
"term" => "hours",
"order" => "a",
"relto" => "last",
"date" => "none"
);

Sorting an Associative Multi-Dimensional Array by Two Keys or Values

We can use the PHP function, array_multisort, to sort multi-dimensional arrays by two fields as follows:

<?php

//Make a $tempArr of sort columns and their data to pass to array_multisort function  
    $tempArr = array();

    foreach($assoc_array as $key=>$val) {
        $tempArr['order'][$key] = $val['order'];
        $tempArr['qty'][$key] = $val['qty'];
    }
// sort by order asc and then qty asc
    array_multisort($tempArr['order'], SORT_ASC, $tempArr['qty'], SORT_ASC,$assoc_array);

?>

Sum It Up!

This was a relatively short ans sweet tutorial on PHP arrays as far as such things often go. There is however a lot more to learn when it comes to working with arrays in PHP. To see all of the PHP array related functions in one place, check out this link:

http://php.net/manual/en/array.sorting.php

Developer’s Guide to Working With Contact Form 7

Today, I had the unfortunate pleasure of working with Contact Form 7. I must admit that it was a little refreshing to see such an under-developed WordPress plugin for a change. I've gotten used to the over-developed monstrosities of today, but CF7 is truly bare-bones when it comes to functionality. It does on basic task and, I assume, does it well, since its such a popular WordPress plugin today. Contact Form 7 allows you to set up a contact form on your website that will email specific information you set up in the form to collect. It is supposed to be simple and sometimes it is. If it works the first time out of the box, you're one of the lucky ones. I had issues. The issues I had were not necessarily the plugin's fault, however, they could have saved me some time with more complete documentation regarding what to do when it fails. My issue ended up being that PHP mail function wasn't functioning, so I downloaded an SMTP plugin to resolve the issue before continuing to develop the code in this guide.

How to retrieve information submitted in Contact Form 7 Forms

The goal of this guide is to teach you how to retrieve data submitted in CF7 forms and work with it in a plugin or from your functions.php file if you prefer it that way.

Why Retrieve Data from Contact Form 7 Forms?

There are a lot of good answers to this question, but it basically depends on your individual needs and desires. The best answer perhaps is to save the form data in a database or log file because CF7 doesn't store any data from form submissions! I've heard there are plugins or add-ons for CF7 that enable CF7 to store it's data in a database, but for the sake of learning how to retrieve data, we won't be using one of those plugins today. Actually, we won't even store the info in a database. Everybody should know how to do that if they are advanced enough to be attempting this guide, so we will save form data to a log file instead, just because, as far as I know, there is no guide available that tells you how to do that as of the time I am writing this guide.

Okay that's enough on why, you can think of your own reason why....let's get to the how!

Retrieving Data From CF7 Forms

The first thing we need to accomplish is to hook into the form when it is submitted somehow. I've found that the action hook named "wpcf7_before_send_mail" works great for this purpose. Here is how to use wpcf7_before_send_mail:

add_action( 'wpcf7_before_send_mail', 'process_contact_form_data' );
function process_contact_form_data( $contact_form ){

}

...that is your basic action and call back function set up. Now all we need is to add some code inside of the empty process_contact_form_data function. We need to gather data submitted in the Contact Form 7 form, so let's look at how we can do that, shall we?

An integral class used in retrieving form data since CF7 Version 3.9 is known as the "WPCF7_Submission" class which includes the "get_instance()" method used to fetch data arrays. To be complete you should check for the class and then use get_instance() to fetch the data like this:

function process_contact_form_data( $contact_form ){

if (!isset($contact_form->posted_data) && class_exists('WPCF7_Submission')) {
    $log .= "posted data set and class exists!\n";
        $submission = WPCF7_Submission::get_instance();
        if ($submission) {
            $log .= "submission exists!\n";
            $formdata = $submission->get_posted_data();
        }
    }

}//end process_contact_form_data function

What the above code does is puts the posted form data into an array named $formdata. To get a specific form field's data you need to use the field's name attribute as a key to the $formdata array. For example, if you used the default CF7 form setup, you would access the submitted name, email, subject and message like this:

$name = $formdata['your-name'];

$email= $formdata['your-email'];

$subj = $formdata['your-subject'];

$name = $formdata['your-message'];

The above code would go inside the above function just before the closing bracket, then you'll need code to write those variables to a log file as we discussed earlier. The basic code to write to a file from PHP looks like this:

$myFile = "/complete/path/cf7_log.txt";
$fh2 = fopen($myFile, 'a') or die("can't open file to append");
$stringData = "form ID: $form_id\n name:$name\n email: $email\n $log\n\n";
fwrite($fh2, $stringData);
fclose($fh2);

Okay! Now we just have to put all the pieces together inside the process_contact_form_data PHP function inside of your plugin file or functions.php file. To keep things safe, I suggest making your own little plugin for this, so that's what I'll do next, create a single file plugin that simply writes CF7 form data to a log file inside the plugin's main folder. Let's call our plugin CF7_logger.

You can easily make the plugin described by piecing together the code snippets in this guide, or you can purchase the entire tested and debugged version from me by emailing linian11@yahoo.com. Good Luck!

 

How to Send SMS Text Messages From PHP

In this PHP tutorial, I'll be showing you how to send text messages to cell phones from a website or app using PHP with a simple HTML form to collect the data. The only drawback to sending messages from PHP is that you typically need to know the receiving party's cell phone carrier in addition to their phone number. The only existing method of getting around having to know the person's cell phone carrier is to use a paid service to send SMS messages such as an SMSC or Short Message Service Center. One provider of SMS services is https://www.twilio.com/.

What are your SMS messaging needs?

This is an important question you should answer before proceeding because if you need to be able to send text messages  with just a phone number and the message content, then you'll need an SMSC like Twilio, otherwise, if you don't mind making the user enter their Cell Phone Carrier name in addition to their phone number in a form to send a text message to them, then the free solution I'm about to show you will work fine for you.

Sending Text Messages From PHP

The basic high level steps to building an application to send out text messages from a web form are as follows:

  1. Built an HTML form that submits to a PHP processing script and that collects the receiving party's phone number, cell carrier name and the text message content.
  2. Create the PHP processing script to receive and process the information gathered in the form and send out the text message, using an email service, to the receiving party.

The entire process in it's simplest form is outlined below:

Write the HTML form. I created a folder named "SMS" and put a new PHP file named "index.php" inside the folder, then added the HTML for the text messaging form as you see here:

<form method="post" action="">
Phone No.: <input type="text" id="ph" name="ph" value="1231231234" /><br />
<br />
Carrier: <select id="ca" name="ca">
<option value="">[Select a Provider]</option><option value="">--Popular Providers--</option><option value="alltel">Alltel Wireless</option>
<option value="@att.txt.net">AT&amp;T</option>
<option value="@myboostmobile.com">Boost Mobile</option>
<option value="@sms.mycricket.com">Cricket</option>
<option value="@messaging.nextel.com">Nextel</option>
<option value="@messaging.sprintpcs.com">Sprint</option>
<option value="@tmomail.net">T-Mobile / Voice Stream</option>
<option value="@tmomail.net">TracFone</option>
<option value="@vtext.com">Verizon Wireless</option>
</select>
<br />
<br />
<textarea rows="5" cols="65" id="msg" name="msg"></textarea>
<br />
<input type="submit" id="sbtsms" name="sbtsms" value="Send Text!" /><br />

</form>

Above is your HTML form, next write some PHP code to process the above form like so:

<?php
if(isset($_POST['ca'])){//if info was submitted, send sms msg:
$ca='';
$ph = $_POST['ph'];
$ca = $_POST['ca'];
$msg = $_POST['msg'];

//if carrier is still blank, set the default carrier(verizon is the most used carrier in the U.S., so....:
if($ca=='')$ca='@vtext.com';

//combine the phone number and the carrier to make the email address to send SMS messages to:
$send_to = $ph.$ca;
echo "Attempting to reach $ph via $ca carrier.....<br />";
echo "Sending Message to $send_to:<br />$msg<hr />";
$sent_sms = mail($send_to, '', $msg);
if($sent_sms){
echo "<h2 style='color:lime'>Message Sent!</h2>";
}else{
echo "<h3 style='color:red'>Oops! Something went wrong, try again later. Make sure you selected the right carrier and phone number.</h3>";
}
}//end if info was submitted, send msg
?>

...the above code goes right after the </form> tag from above, then save the file as sms.php and upload it to your server and try to send yourself a message. If you use a different carrier than the ones provided in the code, you may need to add some options. A complete list of carriers can be downloaded online from https://davidwalsh.name/demo/SMS-Carriers.pdf

Here is a ready to copy and paste version you can simply copy all the below code into a file and save it as a .php file and it should work out of the box for the carriers listed in the provided dropdown:

<!DOCTYPE html>
<html>
<head>
<title>SMS via PHP</title>
</head>
<body>
<h1>Send Text Messages Online for Free!</h1>
<p>Yes, you can send SMS or Text messages online for free using PHP and sendmail. You simply have to provide all of the information requested in the below form and click the send text button to deliver it to the phone number you entered. You must know the receiving party's phone carrier for this to work however.</p><p>Tip: you can usually get the carrier of someone's phone by looking at an email they have sent you and reading the part after the @ symbol in the "From" email in the header of the email you received.</p>

<form method="post" action="">
Phone No.: <input type="text" id="ph" name="ph" value="1231231234" /><br />
<br />
Carrier: <select id="ca" name="ca">
<option value="">[Select a Provider]</option><option value="">--Popular Providers--</option><option value="alltel">Alltel Wireless</option>
<option value="@att.txt.net">AT&amp;T</option>
<option value="@myboostmobile.com">Boost Mobile</option>
<option value="@sms.mycricket.com">Cricket</option>
<option value="@messaging.nextel.com">Nextel</option>
<option value="@messaging.sprintpcs.com">Sprint</option>
<option value="@tmomail.net">T-Mobile / Voice Stream</option>
<option value="@tmomail.net">TracFone</option>
<option value="@vtext.com">Verizon Wireless</option>
</select>
<br />
<br />
<textarea rows="5" cols="65" id="msg" name="msg"></textarea>
<br />
<input type="submit" id="sbtsms" name="sbtsms" value="Send Text!" /><br />

</form>

<?php
if(isset($_POST['ca'])){//if info was submitted, send sms msg:
$ca='';
$ph = $_POST['ph'];
$ca = $_POST['ca'];
$msg = $_POST['msg'];

//if carrier is still blank, set the default carrier(verizon is the most used carrier in the U.S., so....:
if($ca=='')$ca='@vtext.com';

//combine the phone number and the carrier to make the email address to send SMS messages to:
$send_to = $ph.$ca;
echo "Attempting to reach $ph via $ca carrier.....<br />";
echo "Sending Message to $send_to:<br />$msg<hr />";
$sent_sms = mail($send_to, '', $msg);
if($sent_sms){
    echo "<h2 style='color:lime'>Message Sent!</h2>";
}else{
    echo "<h3 style='color:red'>Oops! Something went wrong, try again later. Make sure you selected the right carrier and phone number.</h3>";
}
}//end if info was submitted, send msg
?>

</body>
</html>

Taking it further

The main drawback to this method is that you have to also know the cell phone carrier, but I've thought of a way to overcome that with a bit of extra coding.  I found this site that takes the phone number in three parameters so for my business line,(234) 650-2011, it would be like this:

http://fonefinder.net/findome.php?npa=234&nxx=650&thoublock=2011

I plan to write a PHP script to resolve the address and scrape the results to get the carrier name. It will be a little complicated because you'll need to get the carrier name and then translate it into the actual email for ea. carrier, but it is definitely possible with some work.

Summary:

That's all there is to it! You can definitely improve upon this version of course as it is just meant to get you started. There is no form authentication and only very limited cell phone carriers listed in the dropdown, but the resource is provided to add more carriers from https://davidwalsh.name/demo/SMS-Carriers.pdf, so feel free to build onto what I've started here and let us know what improvements you've made in a comment so others can learn from it!

 

How to Clone a MYSQLI Table From Command Line

Here are the two commands you can run from the Mysqli Command Prompt to successfully clone a database table. In the example we will name our tables new_table_name and old_table_name where old_table_name is the table we wish to clone. This is the best way I have found to-date to create a backup of a mysqli table from the command line:

CREATE TABLE new_table_name LIKE old_table_name;
INSERT new_table_name SELECT * FROM old_table_name;

Be sure to enter line one above and press enter, then do the same with the second line.

How to Figure Out Relative Humidity with PHP

Today, I had to calculate relative humidity using PHP and I have documented my findings below:

First, let's just use an example situation where we have a temperature of 60.1 and a dew point of 42.7, both in Fahrenheit, so...:

dew point in Fahrenheit: 42.7

temperature in Fahrenheit: 60.1

1) The first step is to convert to Celsius using the following formulas
Tc=5.0/9.0*(Tf-32.0)

Tdc=5.0/9.0*(Tdf-32.0)

Formulas explained:
Tc=air temperature in degrees Celsius, Tf=air temperature in degrees Fahrenheit

Tdc=dewpoint temperature in degrees Celsius

Tdf=dewpoint temperature in degrees Fahrenheit

Notice: If your temperature and dewpoint are in degrees Celsius, you can skip step 1 and proceed to step 2.

answer for equations:
Temp in Celsius: 15.61

Tc=5.0/9.0*(Tf-32.0)
5.0/9.0*(60.1-32.0)
5.0/9.0*28.1
0.5555555555555556 * 28.1 = 15.61111111111111

dewpoint in Celsius: 5.94
5.0/9.0*(Tdf-32.0)
5.0/9.0*10.7
0.5555555555555556 *  10.7 = 5.944444444444444

2) calculate saturation vapor pressure(Es) and actual vapor pressure(E) in millibars:
NOTE: first line is the equation and the subsequent lines represent one step solved at a time:
Es=6.11*10.0**(7.5*Tc/(237.7+Tc))
Es=6.11*10.0**(7.5*15.61/(237.7+15.61))
Es=61.1 ** (7.5*15.61/(237.7+15.61))
Es=61.1 ** (117.075/253.31)
Es = 61.1**0.4621807271722395
Es = 6.6907349413770067935260257174923

E=6.11*10.0**(7.5*Tdc/(237.7+Tdc))
E=6.11*10.0**(7.5*5.94/(237.7+5.94))
E=61.1 ** (7.5*5.94/(237.7+5.94))
E=61.1 ** (44.55/243.64)
E=61.1 ** 0.1828517484813659497619438515843
E = 2.1211957981192776150462474985589

3)  Once you have the saturation vapor pressure and actual vapor pressure, relative humidity(RH) can be computed by dividing the actual vapor pressure by the saturation vapor pressure and then multiplying by 100 to convert the quantity to a percent:
RH =(E/Es)*100
RH =(2.1211957981192776150462474985589/6.6907349413770067935260257174923)*100
RH = 0.31703479762758596814611114744566 * 100
RH = 31.703479762758596814611114744566%
SO... Humidity is 31.7%

And note here that ** means to the power of. I figured I'de clue anyone in that is as ignorant is I was when I had to figure it out.

How to Search and Replace File Names

A lot of times I am required to rename large quantities of files according to various rules. Sometimes this task can take hours to complete. Today I had a job requiring me to rename all files in a program that contained "xi" with "nap". The program had thousands of files in a dozen different directories. It would have taken days for me to go through them all manually and replace ea. occurrence of "xi" in the file names with "nap", so I tested several tools to help me do the job. The most capable tool I found was named simply "ReNamer" and can be downloaded from:
https://www.den4b.com/products/renamer

I downloaded the "portable" version of ReNamer version 6.7 Here is a screenshot of ReNamer's simply UI:
ReNamer

How to Download and Open ReNamer for First Use

First things first, so here is how to get started:

Use the link https://www.den4b.com/products/renamer to download the portable version of ReNamer and it will download a zip file to your PC. Place the file on your desktop and right click it and select "Extract All". Windows will extract the files and probably open the folder for you. Then click on renamer.exe to start the app. You will see the UI as in the above image. I like using this portable version because it is very light-weight and can be used on any PC. When I'm done using it, I simply delete the entire unzipped folder but I save the .zip folder I downloaded so I can use it again when needed and it doesn't waste any space on my PC when it's not in use. Next time I need it, I simply extract the files again and use it. Then I delete the folder when done again.

Find and Replace Text in File Names of Many Files at Once

It is easy as pie to use too! It only took me a couple of test runs to achieve the renaming rules I needed to do the job at hand. Just  click where it says "Click here to add a rule" and add a rule. I needed to find and replace text in the file names, so in my case, I clicked on "replace" in the left panel so the add rule screen looks like this:

renamerules

 

All I had to do was simply enter "xi" in the "find" field and "nap" in the "replace" field and click the "Add Rule" button to save your new rule. Then all you have to do is drag the folder containing all the files you want to rename into the UI as in the first image above, where it says "Drag Your Files Here". Then it gives you a preview of what files it will rename. Once you are happy with how it's doing the renaming, click the "Rename" button in the upper right corner of the UI and it will rename all of the files just like it showed you. If you have tested any of the other features of this tool, please comment below and describe your experience!

Understanding ARIA Click Button to Show or Hide Content Example Code

Understanding ARIA

ARIA stands for "Accessible Rich Internet Applications". Also known as the WAI-ARIA standard, it is a standard developed to help coders to provide proper semantics for custom widgets and to make them accessible, usable, and interoperable with assistive technologies for people with disabilities. To be clear, ARIA doesn't add functionality to an object. It adds roles and states that assist in identifying the intent and state of an object. However, usually JavaScript code is still needed to add any dynamic action to that object. I state this clearly at the top of this post because at first, I was under the impression that ARIA also added certain functionalities to HTML objects and was seriously disappointed when I found out otherwise. For example, when ARIA is used on a button that hides and shows content in a div, it only defines the roles and states of the button and corresponding div. JavaScript is still needed to do that work of hiding and showing the div in question.

Example Code

Here is an example of correctly implementing ARIA controls when making a button that hides and shows a div on the click of your mouse. It also binds the space bar and enter key to the div as well, so pressing either of those keys toggles the visibility of the div as well. Without any further ado, the code:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Aria Examples</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

<style>
div.topic {
    display: none;
    margin-bottom: 1em;
    padding: .25em;
    border: black thin solid;
    background-color: #EEEEFF;
    width: 40em;
}
</style>
</head>
<body>

<p class="button">
    <button id="button1" class="buttonControl" aria-controls="t1" aria-expanded="false"><span>Show</span> Topic 1</button>
</p>

<div id="t1" class="topic" role="region" tabindex="-1" >
    Topic 1 is all about being Topic 1 and may or may not have anything to do with other topics.
</div>

<script>
$(document).ready(function() {

   var hs1 = new hideShow('button1');
  // var hs2 = new hideShow('button2');
  // var hs3 = new hideShow('button3');
  // var hs4 = new hideShow('button4');
 
}); // end ready()

//
// function hideShow() is the constructor for a hideShow widget. it accepts the html ID of
// an element to attach to.
//
// @param(id string) id is the html ID of the element to attach to
//
// @return N/A
//
function hideShow(id) {

   this.$id = $('#' + id);
   this.$region = $('#' + this.$id.attr('aria-controls'));

   this.keys = {
               enter: 13,
               space: 32
               };

   this.toggleSpeed = 100;

   // bind handlers
   this.bindHandlers();

} // end hidShow() constructor

//
// Function bindHandlers() is a member function to bind event handlers to the hideShow region
//
// return N/A
//
hideShow.prototype.bindHandlers = function() {

   var thisObj = this;

   this.$id.click(function(e) {

      thisObj.toggleRegion();

      e.stopPropagation();
      return false;
   });
}

//
// Function toggleRegion() is a member function to toggle the display of the hideShow region
//
// return N/A
//
hideShow.prototype.toggleRegion = function() {

      var thisObj = this;

    // toggle the region
    this.$region.slideToggle(this.toggleSpeed, function() {

      if ($(this).attr('aria-expanded') == 'false') { // region is collapsed

        // update the aria-expanded attribute of the region
        $(this).attr('aria-expanded', 'true');

        // move focus to the region
        $(this).focus();

        // update the button label
        thisObj.$id.find('span').html('Hide');

      }
      else { // region is expanded

        // update the aria-expanded attribute of the region
        $(this).attr('aria-expanded', 'false');

        // update the button label
        thisObj.$id.find('span').html('Show');
      }
    });

} // end toggleRegion()
</script>
</body>
</html>

Thank you oaa-accessibility.org for providing me with enough knowledge to create and use the above example! They have the best example code for ARIA usage that I could find online after many Google searches. See their complete list of example ARIA examples at http://oaa-accessibility.org/

ARIA and WordPress

I noticed ARIA controls for the first time in the header.php file for my WordPress theme. I was trying to fix a mobile navigation menu and thought couldn't find the code that makes the menu appear on mobile devices when the button is clicked and thought ARIA had something to do with it. I was basically wrong. ARIA code was only present to mark the navigation menu and make it's role and states readily accessible. It is after all an accessibility feature.

Summary

So ARIA and the WAI-ARIA standard are used to enable more accessible HTML markup for disabled people. While it is a great initiative, it doesn't add much dynamic functionality to your HTML objects, JavaScript is still needed for that. ARIA combined with HTML, CSS and JavaScript can be used to make accessible web pages more user-friendly.

Get IP Address from Domain Name

Domain name:

Returns the IP address associated with the domain name you enter into the form. It will return a list of IP addresses if more than one is associated with the given domain name.

PHP Function explode

The PHP explode function is one of the most used PHP functions, at least by me. It is used to turn a string into an array. Let's say you had the following string:

$str = "Pizza, Pork, Ham, Sub, Chicken, Lamb, Rice, Noodles";

You can very easily turn such a string into an array using the PHP explode function like this:

$foods = explode(",", $str);

Now you have the equivalent of this:

$foods = array('Pizza', 'Pork', 'Ham', 'Sub', 'Chicken', 'Lamb', 'Rice', 'Noodles');

Here is a complete PHP example that demonstrates the use of explode:

$str = "Pizza, Pork, Ham, Sub, Chicken, Lamb, Rice, Noodles";

$foods = explode(",", $str);

foreach($foods as $food){

echo $food."<br />";

}

The above example simply uses explode to break the $str string into the $foods array by splitting the string up by the commas.

Arguments

The PHP explode function takes two arguments:

explode('split-by', $string);

  • split-by - can be any value in $string that you want to separate the string into an array by.
  • $string - is the string you want to turn into an array.

Summary

This post should give you a good idea of how to use the explode function. If not, feel free to post your comments on this post and I'll be happy to explain. Also, feel free to publish your own example code that users explode in the comments.

How to Remove Slugs for a Custom Post Type in WordPress

So, back in the day, not to long ago, one could remove slugs in a CPT(Custom Post Type), simply by adding the following line to the arguments array when calling the register_post_type function:

'rewrite' => array('slug' => ''),//NOT THE SOLUTION!

However, that no longer works as of WordPress version 4.7.?, so I put together the following two function from various other solutions I've seen. This solution works for a single post type or multiple post types. I am publishing it because I haven't seen any elsewhere that could handle more than one CPT at a time. I wrote this to remove slugs for two CPTs, but you could us it for one or as many as you want, the two functions are as follows and can be added to functions.php or, as I do, to a plugin file, below the register_post_type functions:

<?php

//Two functions to remove slug from team_member and event CPTs:
function remove_evil_slugs($post_link, $post, $leavename) {

    if('team_member' != $post->post_type || 'event' != $post->post_type ||'publish' != $post->post_status) {
        return $post_link;
    }

    $post_link = str_replace('/' . $post->post_type . '/', '/', $post_link);

    return $post_link;
}
add_filter('post_type_link', 'remove_evil_slugs', 10, 3);

function  parse_evil_slugs($query) {

    if(!$query->is_main_query() || 2 != count($query->query) || !isset($query->query['page'])) {
        return;
    }

    if(!empty($query->query['name'])) {
        $query->set('post_type', array('post', 'team_member', 'event', 'page'));
    }
}
add_action('pre_get_posts', 'parse_evil_slugs');

?>

Those are the two functions that accomplish the goal. Simply replace all occurrences of "team_member" and "event" with the names of your own custom post types. If you use any rewrite rules in the args parameter for the register_post_type function, comment it out. For example if you see anything that looks like this:

'rewrite' => array('slug' => ''),

or

'rewrite' =>false,

...either comment it out of delete it all together from the function that creates the custom post type(CPT).

Update 3-27-2017

I've noticed that on some servers, the above solution no longer works, so I was able to program a new solution for NGINX servers. If your server is not an NGINX server, I found a solution here that should work for all even though it is a bit outdated: https://github.com/jonbish/remove-slug-from-custom-post-type. The code below is code I wrote based on that link but updated for nginx servers:

<?php
class JAFTY_CPT_SlugKiller{
    
    static $_s = null;
    private $htaccess_tag = 'SLUG KILLER REMOVE SLUG RULES';
    
    public function __construct() {
        $this->rewrite_rules();
        
        add_action('wp_insert_post', array(&$this, 'post_save'));

        add_filter('post_type_link', array(&$this, 'remove_slug'), 10, 3);
        
    }
    
    
    static public function init() {
        if (self::$_s == null) {
            self::$_s = new self();
        }
        return self::$_s;
    }
    
    static public function flush_rewrite_rules() {
        $jafty_o = self::init();
        $jafty_o->rewrite_rules(true);    
        //$jafty_o->add_rules_htaccess();
    }

    public function post_save($post_id) {
        global $wp_post_types;
        $post_type = get_post_type($post_id);
        foreach ($wp_post_types as $type=>$custom_post) {
            if ($custom_post->_builtin == false && $type == $post_type) {
                $this->rewrite_rules(true);
                //$this->add_rules_htaccess();
                flush_rewrite_rules();
            }
        }
    }
    
    public function remove_slug($permalink, $post, $leavename) {
        global $wp_post_types;

        foreach ($wp_post_types as $type=>$custom_post) {
            if ($custom_post->_builtin == false && $type == "team_member") {
                $custom_post->rewrite['slug'] = trim($custom_post->rewrite['slug'], '/');
                $permalink = str_replace(get_bloginfo('url') . '/' . $custom_post->rewrite['slug'] . '/', get_bloginfo('url') . "/", $permalink);
            }
        }
        return $permalink;
    }
    
    public function rewrite_rules($flash = false) {
        global $wp_post_types, $wpdb;
        foreach ($wp_post_types as $type=>$custom_post) {
            if ($custom_post->_builtin == false && $type == "team_member") {
                $querystr = "SELECT {$wpdb->posts}.post_name
                                FROM {$wpdb->posts}
                                WHERE {$wpdb->posts}.post_status = 'publish'
                                        AND {$wpdb->posts}.post_type = '{$type}'
                                        AND {$wpdb->posts}.post_date < NOW()";
                $posts = $wpdb->get_results($querystr, OBJECT);
                foreach ($posts as $post) {
                    $regex = "{$post->post_name}\$";
                    add_rewrite_rule($regex, "index.php?{$custom_post->query_var}={$post->post_name}", 'top');            
                }
            }
        }
        if ($flash == true)
            flush_rewrite_rules(false);
    }
    
    
    private function add_rules_htaccess() {
        global $wp_post_types;
        $suffix = get_option('jafty_permalink_customtype_suffix');
        $write = array();
        $htaccess_filename = ABSPATH . '/.htaccess';
        if(is_readable($htaccess_filename)){
            $htaccess = fopen($htaccess_filename, 'r');
            $content = fread($htaccess, filesize($htaccess_filename));
            foreach ($wp_post_types as $type=>$custom_post) {
                $rewrite_rule = (!empty($suffix))
                            ? "RewriteRule ^{$custom_post->query_var}/(.+)/\$ /\$1\.{$suffix} [R=301,l]"
                            : "RewriteRule ^{$custom_post->query_var}/(.+)/\$ /\$1 [R=301,L]";
                if (strpos($content, $rewrite_rule) == false && $custom_post->_builtin == false)
                    $write[] = $rewrite_rule;
            }
            fclose($htaccess);
        }else{
            add_action('admin_notices', array(&$this, 'compatibility_notice'));
            return;
        }
        
        if (!empty($write) && is_writable($htaccess_filename)) {
            $new_rules = '# BEGIN ' . $this->htaccess_tag . PHP_EOL;
            $new_rules .= str_replace('$', '\\$', implode(PHP_EOL, $write)) . PHP_EOL;
            $new_rules .= '# END ' . $this->htaccess_tag;
            if (strpos($content, "# BEGIN {$this->htaccess_tag}") === false) {
                file_put_contents($htaccess_filename, $new_rules . PHP_EOL . PHP_EOL . $content);
            }
            else {
                $pattern = "/# BEGIN {$this->htaccess_tag}.*?# END {$this->htaccess_tag}/ims";
                $content = preg_replace($pattern, $new_rules, $content);
                file_put_contents($htaccess_filename, $content);
            }
        }else if(!is_writable($htaccess_filename))
            add_action('admin_notices', array(&$this, 'compatibility_notice'));
    }//end add_rules_htaccess function

    
    public function compatibility_notice() {
        global $wp_post_types;
        $rules = '';
        foreach ($wp_post_types as $type=>$custom_post) {
            if ($custom_post->_builtin == false && $type == "team_member") {
                $slug = str_replace('/', '', $custom_post->rewrite['slug']);
                $rules .= 'RewriteRule ^' . $slug . '/(.+)$ /$1 [R=301,L]<br />';
            }
        }
        
        echo '<div class="error fade" style="background-color:red;"><p><strong>Remove Slug Custom post type error!</strong><br />.htaccess is not writable, please add following lines to complete your installation: <br />'.$rules.'</p></div>';
    }
}//End JAFTY_CPT_SlugKiller class to remove slugs from team_member CPT

//actions and hooks for above class to remove slug from team_member CPT:
add_action('init', array('JAFTY_CPT_SlugKiller', 'init'), 99);
//the following two lines make it so you don't have to manually go to settings/permalinks and re-save settings for links to work:
register_activation_hook( __FILE__, array('JAFTY_CPT_SlugKiller', 'flush_rewrite_rules') );
register_deactivation_hook( __FILE__, array('JAFTY_CPT_SlugKiller', 'flush_rewrite_rules') );
//END CODE TO ENABLE NO SLUG FOR team_member CPT

?>

The above code can be added to a plugin file or your themes functions.php.

NOTE: although the above code hasn't been thoroughly tested on non-nginx servers, it should work if you un-comment the two lines that call the add_rules_htaccess function. Simply do a search for "add_rules_htaccess" and remove the "//" before it in two occurrences and this code should work on any server as long as your .htaccess file is writable.

Get the Nth Weekday of Any Month and Year with JavaScript

In today's challenge for a client of mine, I had to write a JavaScript function that returns the Nth Weekday of any given month and year. For example it can tell you what the 1st Monday is in December, 2017. It can do past, present and future so if you needed to know the 4th Friday in August of 1910, this function can tell you. If you need to know what the second Sunday of January 2050 is going to be, this JavaScript function will tell you!

Without any further time wasting, here is the JavaScript code that can determine the first, second, third, fourth or even fifth Monday, Tuesday, Wednesday, Thursday, Friday, Saturday or Sunday for any month and year you pass it:

JavaScript Code Returns Nth Weekday for any Given Month & Year:

<script>
/* JavaScript getMonthlyWeekday Function:
 * Written by Ian L. of Jafty.com
 *
 * Description:
 * Gets Nth weekday for given month/year. For example, it can give you the date of the first monday in January, 2017 or it could give you the third Friday of June, 1999. Can get up to the fifth weekday of any given month, but will return FALSE if there is no fifth day in the given month/year.
 *
 *
 * Parameters:
 *    n = 1-5 for first, second, third, fourth or fifth weekday of the month
 *    d = full spelled out weekday Monday-Friday
 *    m = Full spelled out month like June
 *    y = Four digit representation of the year like 2017
 *
 * Return Values:
 * returns 1-31 for the date of the queried month/year that the nth weekday falls on.
 * returns false if there isn't an nth weekday in the queried month/year
*/
function getMonthlyWeekday(n,d,m,y){
var targetDay, curDay=0, i=1, seekDay;
    if(d=="Sunday") seekDay = 0;
    if(d=="Monday") seekDay = 1;
    if(d=="Tuesday") seekDay = 2;
    if(d=="Wednesday") seekDay = 3;
    if(d=="Thursday") seekDay = 4;
    if(d=="Friday") seekDay = 5;
    if(d=="Saturday") seekDay = 6;
while(curDay < n && i < 31){
    targetDay = new Date(i++ + " "+m+" "+y);
    if(targetDay.getDay()==seekDay) curDay++;
}
if(curDay==n){
targetDay = targetDay.getDate();
return targetDay;
}else{return false;}
}//end getMonthlyWeekday JS function
</script>
<a href="JavaScript:var dy = getMonthlyWeekday(1,'Sunday','March', 2017);alert('1st Sunday in March, 2017 falls on March, '+dy);">Get first sunday in March, 2017</a><br />
<a href="JavaScript:var dy = getMonthlyWeekday(2,'Sunday','March', 2017);alert('2nd Sunday in March, 2017 falls on March, '+dy);">Get second sunday in March, 2017</a><br />
<a href="JavaScript:var dy = getMonthlyWeekday(5,'Sunday','March', 2017);alert('5th Sunday in March, 2017 falls on March, '+dy);">Get fifth sunday in March, 2017</a><br />
<a href="JavaScript:var dy = getMonthlyWeekday(4,'Friday','April', 2017);alert('4th Friday in April, 2017 falls on April, '+dy);">Get 4th Friday in April, 2017</a><br />
<a href="JavaScript:var dy = getMonthlyWeekday(5,'Monday','April', 2017);alert('5th Monday in April, 2017 falls on April, '+dy);">Get 5th Monday in April, 2017</a><br />
<a href="JavaScript:var dy = getMonthlyWeekday(3,'Wednesday','October', 1995);alert('3rd Wednesday in October, 1995 falls on October, '+dy);">Get 3rd Wednesday in October, 1995</a><br />
<a href="JavaScript:var dy = getMonthlyWeekday(4,'Wednesday','October', 1995);alert('4th Wednesday in October, 1995 falls on October, '+dy);">Get 4th Wednesday in October, 1995</a><br />
<a href="JavaScript:var dy = getMonthlyWeekday(4,'Wednesday','May', 1975);alert('4rd Wednesday in May, 1975 falls on May, '+dy);">Get 4th Wednesday in May, 1975</a><br />

Summary

That will do the job! All you have to do is copy and paste the above code in green into a blank text file and name it something like weekdays.html or test.html and open it in any web browser to test the code. Then feel free to alter it to fit your exact needs. Have fun!

 

 

 

How to get Locations in a 50 Mile Radius with Google Maps API v.3

In this post, I will demonstrate how to make getting locations within a given radius simple. In order to make everyone understand, I will break it down into smaller, easy to read steps.

The Distance-Matrix

The key to creating a solution that gets all locations within a given distance radius is to use the Google Maps API Distance-Matrix. Let's examine a simple example request to the Distance-Matrix Json API:

Single Destination URL Example:

https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=44311&destinations=45735&key=Your_API_Key

copy and paste the above URL into a web browser and replace the text, Your_API_Key, with your google Maps V.3 API key. If you don't have an API key yet, they are surprisingly easy to obtain from https://developers.google.com/maps/web-services/

The Response:

The URL above will return a Json response of:

{
   "destination_addresses" : [ "Guysville, OH 45735, USA" ],
   "origin_addresses" : [ "Akron, OH 44311, USA" ],
   "rows" : [
      {
         "elements" : [
            {
               "distance" : {
                  "text" : "166 mi",
                  "value" : 267653
               },
               "duration" : {
                  "text" : "2 hours 30 mins",
                  "value" : 8989
               },
               "status" : "OK"
            }
         ]
      }
   ],
   "status" : "OK"
}

Notice that I only used zip codes for orgin and destination in my example URL. You can use any acceptable address format instead of just a zip code of you like, but I find that for getting distances, zip codes work quite efficiently. Examples of acceptable addresses include:

  • Cleveland, OH
  • 593 Brown Street, Akron, Ohio
  • 44319
  • Akron, Ohio 4319
  • 593 Brown Street, Akron, Ohio 44311
  • USA
  • Amsterdam

Those are just a few acceptable addresses that could be used for either the destination or orgin parameters in the URL used for an API request to the Distance-Matrix.

 

Multiple Destination URL Example:

One thing that's important to know about the Distance-Matrix API is that there are limits on it's free usage. At the time of writing this post, the limits were 2500 elements per day. A single request may take up several elements however, so be careful. However it is much more practical to include several destination addresses in a single request because it will make your script run significantly quicker. Here is a simple example that will return a response with two destination distances using just zip codes for addresses again:

https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=44311&destinations=45735|44319&key=Your_API_Key

This the json response will include the distances for both zip codes or addresses passed in the URL. Here is the response from the above URL with two destinations:

{
   "destination_addresses" : [ "Guysville, OH 45735, USA", "Akron, OH 44319, USA" ],
   "origin_addresses" : [ "Akron, OH 44311, USA" ],
   "rows" : [
      {
         "elements" : [
            {
               "distance" : {
                  "text" : "166 mi",
                  "value" : 267653
               },
               "duration" : {
                  "text" : "2 hours 30 mins",
                  "value" : 8989
               },
               "status" : "OK"
            },
            {
               "distance" : {
                  "text" : "8.9 mi",
                  "value" : 14385
               },
               "duration" : {
                  "text" : "14 mins",
                  "value" : 836
               },
               "status" : "OK"
            }
         ]
      }
   ],
   "status" : "OK"
}

Parsing Json data with PHP

The next task is to fetch a response from PHP and parse the Json data in your PHP code. Lets look at a practical example using our first URL example above that has a single zip code as the destination address and also a single zip code for the orgin. Here is the PHP code to make the request, get a Json response and parse the distance from that response:

<?php
$url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=44311&destinations=45735&key=Your-API-Key";

    //fetch json response from googleapis.com:
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $response = json_decode(curl_exec($ch), true);
    //If google responds with a status of OK
    //Extract the distance text:
    if($response['status'] == "OK"){
        $dist = $response['rows'][0]['elements'][0]['distance']['text'];
        echo "<p>Dist: $dist</p>";
    }
?>

Now lets have a look at how we would modify the above code for a request with two destination addresses instead of one:

<?php
$url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=44311&destinations=45735|44319&key=Your-API-Key";

    //fetch json response from googleapis.com:
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $response = json_decode(curl_exec($ch), true);
    //If google responds with a status of OK
    //Extract the distance text:
    if($response['status'] == "OK"){
        $dist = $response['rows'][0]['elements'][0]['distance']['text'];
        echo "<p>Dist: $dist</p>";
        $dist2 = $response['rows'][0]['elements'][1]['distance']['text'];
        echo "<p>Dist2: $dist2</p>";
    }
?>

If you insert your own API key in the above code and run it from your own server, the response would look like this:

Dist: 166 mi

Dist2: 8.9 mi

Notice the difference in the $dist and $dist2 variable values. The key of the elements is different. The first distance is stored in elements[0] while the second is stored in elements[1]. If we had three destinations in the request, the third would be in elements[2] and so on...

How to figure out the values

So, what if you want more than just distance from the returned json element? Lets examine the returned data and figure out how to get it into PHP variables. In the example code above, try placeing this code right after the line that reads "if($response['status'] == "OK"){" and it will show you what the PHP array looks like after the Json response was converted to a PHP object:

        echo "<pre>";
        print_r($response);
        echo "</pre>";

The above code inserted into the last code example would look like:

<?php
$url = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=44311&destinations=45735|44319&key=Your-API-Key";

    //fetch json response from googleapis.com:
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $response = json_decode(curl_exec($ch), true);
    //If google responds with a status of OK
    //Extract the distance text:
    if($response['status'] == "OK"){
        echo "<pre>";
        print_r($response);
        echo "</pre>";
        $dist = $response['rows'][0]['elements'][0]['distance']['text'];
        echo "<p>Dist: $dist</p>";
        $dist2 = $response['rows'][0]['elements'][1]['distance']['text'];
        echo "<p>Dist2: $dist2</p>";
    }
?>

Again, replace "Your-API-Key" in the URL variable with your own key and run the code. You should see results like this:

Array
(
    [destination_addresses] => Array
        (
            [0] => Guysville, OH 45735, USA
            [1] => Akron, OH 44319, USA
        )

    [origin_addresses] => Array
        (
            [0] => Akron, OH 44311, USA
        )

    [rows] => Array
        (
            [0] => Array
                (
                    [elements] => Array
                        (
                            [0] => Array
                                (
                                    [distance] => Array
                                        (
                                            [text] => 166 mi
                                            [value] => 267653
                                        )

                                    [duration] => Array
                                        (
                                            [text] => 2 hours 30 mins
                                            [value] => 8989
                                        )

                                    [status] => OK
                                )

                            [1] => Array
                                (
                                    [distance] => Array
                                        (
                                            [text] => 8.9 mi
                                            [value] => 14385
                                        )

                                    [duration] => Array
                                        (
                                            [text] => 14 mins
                                            [value] => 836
                                        )

                                    [status] => OK
                                )

                        )

                )

        )

    [status] => OK
)

Dist: 166 mi

Dist2: 8.9 mi

First you see the array printed out from the response after it's converted from Json to a PHP array, then you see the two distances printed to the screen in the last two lines. Examine the array closely and you can figure out the correct keys to use to pick out specific values from the array in your PHP code.

More info on the Distance-Matrix can be found at https://developers.google.com/maps/documentation/distance-matrix/start

How to Do Something Every Nth Iteration in a PHP Loop

If you're a PHP coder, you are sure to come across a scenario from time to time where you have a PHP loop and you need to execute specific code every other time the loop iterates or every four times the loop iterates etc.

Using the Modulus Operator

The key to making something happen every nth time in a loop is the modulus operator or %.

A common scenario where you would need to do something in a loop every fourth or fifth iteration only is when you are printing a series of HTML elements. Let's pretend we have 12 items in an array and we want to print them in groups of four per line. While printing the array you will need to print the <br /> tag every four iterations if you want four items on each line.  The code inside your PHP loop would look like this:

if($i % 4 == 0){echo "<br />";}

Now let's put this to the test with some test code using the above scenario. Below I fill an array with 12 words and print them out in a loop four per line:

<?php
$words = array('one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve');
$noof = count($words);
$i=0;
for($ii=0;$ii<$noof;$ii++){
    $i++;
    echo $words[$ii];
    echo " ";
    if($i % 4 == 0){echo "<br />";}
}//end for loop
?>

Noticed I used $ii in the for loop and set a second variable, $i for using with the modulus line. This is because $ii will start at zero and we need to start at 1 in this case. The results of the above code would be:

one two three four
five six seven eight
nine ten eleven twelve

Today however, I came across a more complex scenario where I was able to also use the modulus operator to save the day. This time I needed to print items from an array inside of HTML list elements. Lets modify the above scenario using lists instead of line breaks to display the array data:

<?php
$words = array('one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve');
$noof = count($words);
$i=0;
for($ii=0;$ii<$noof;$ii++){
    $i++;
    if($i % 4 == 1)echo "<ul>";
    echo "<li>";
    echo $words[$ii];
    echo "</li>";
    if($i % 4 == 0)echo "</ul>";
}//end for loop
?>

The output of the above PHP code would be:

  1. one
  2. two
  3. three
  4. four
  1. five
  2. six
  3. seven
  4. eight
  1. nine
  2. ten
  3. eleven
  4. twelve

 

The results were four separate ordered lists  as you can see. But how did we do this? Notice the difference in the modulus operator use for the opening <ol> tag and then for the closing </ol> tag.

The tricky part here is we can't use if($i % 4 == 0) on the opening <ol> tag because if you think about it you don't need <ol> printed on 4,8 and 12 which is what if($i % 4 == 0) would produce. In this case, we need to print an opening <ol> tag on iterations 1,5,9 and 13, so we try something else. Here is how I figured this problem out:

First a simple demo of the modulus operator as we first used it:

<?php
$i=0;
for($ii=0;$ii<20;$ii++){
    $i++;
    if($i % 4 == 0)echo "$i<br />";
}//end for loop
?>

The above code results in:

4
8
12
16
20

Which is fine for printing the end </ol> tags, but will not work for printing the opening <ol> tags as I explained earlier, so I tried this instead:

<?php
$i=0;
for($ii=0;$ii<20;$ii++){
    $i++;
    if($i % 4 == 1)echo "$i<br />";
}//end for loop
?>

This time the above code printed out:

1
5
9
13
17

So, if we used a one instead of a zero like if($i % 4 == 1), then it would execute the code in the if statement on iterations 1,5,9 and 13 just as we need! Just for fun, let's see what happens if we use a two instead of zero this time like if($i % 4 == 2). For example:

<?php
$i=0;
for($ii=0;$ii<20;$ii++){
    $i++;
    if($i % 4 == 2)echo "$i<br />";
}//end for loop
?>

This time the results would be:

2
6
10
14
18

so using a 2 to compare 4 modulus with results in the code executing on iterations 2,6,10....etc, but notice in each example where we use 4 as the first number, all the numbers are increments of 4 and when we use zero as the last number we get 4,8,12.... When we use 1 we get 1,5,9.... Finally if we use 2 we get 2,6,10.... From what we have learned I think it's safe to assume that we we used 3, the results would be 3,7,11,15....and so on.

Summary

I hope this article sheds some light on how to use the Modulus operator with PHP. Feel free to experiment by using other numbers in place of where I used 4 each time too! For example, if you want to do something every 5 times instead, you would use something like if($i % 5 == 0). Have fun with it!