PHP


Email Monthly Spreadsheet Reports from WordPress

I’ve been working on a project where we have a very custom Customer Service form and we wanted to start sending out monthly excel reports with all the issues from the past month.

The form is a bit too custom to switch over to a plugin, so I opted to do the set-up by hand.  At the start the form was only sending out emails. So first I needed to create a custom post type to hold all the responses.  you can create one by adding the following code to your functions.php file.  WordPress has a breakdown of all the arguments in their documentation.

1
2
3
4
5
6
7
8
9
10
11
//Support Content Type
$args = array(
    'label'              => 'Support Request',
    'show_ui'             => true,
    'show_in_admin_bar'   => false,
    'public'              => false,
    'menu_position'       => 25,
    'menu_icon'           => 'dashicons-editor-help',
    'supports'            => array( 'title','editor','custom-fields')
);
register_post_type( 'support_request', $args );

Next, I needed to add in the following after sending the email off on the custom form page.  I’m setting the post title and post message to be identical to the email title and body that’s already been generated by the form.  I’m also adding in two custom fields “name” and “email.”  Those are just for example, you can add as you like and your form will likely dictate those.

1
2
3
4
5
6
7
8
9
//add to support request content type
$postarr = array(
    'post_title' => $title,
    'post_content'=> $message,
    'post_type' => 'support_request'
);
$post_id = wp_insert_post( $postarr );
update_post_meta($post_id,'Name',$_REQUEST['customer-name']);
update_post_meta($post_id,'Email',$_REQUEST['customer-email']);

After that, I created a page to generate the excel file.  I created a custom “generate_customer_report_excel.php” in my theme’s directory so it wouldn’t fatten up my functions.php file.  I’m going to create the filename ($output_filename) in the functions.php file to keep things DRY. If you want a frequency different then 30 days, you’ll need to change the “‘after’ => ‘- 30 days‘” line. You will also need to create a “support_requests” folder in your uploads folder. Here’s an example of what that page looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//build your query 
$args = array(
    'post_type' => 'support_request',
    'post_status' => array('publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit'),
    'posts_per_page' => -1,
    'date_query'    => array(
        'column'  => 'post_date',
        'after'   => '- 30 days'
    )
);
 
query_posts($args);
 
// Prepare our csv download
$output_handle = fopen(WP_CONTENT_DIR . '/uploads/support_requests/'.$output_filename ,"w");
 
// Insert header row
fputcsv( $output_handle, ['Name','Email','Message']);
 
// The Loop
while ( have_posts() ) : the_post();
    $id = get_the_ID();
    $row = array(
        get_post_meta( $id, 'Name', false )[0],
        get_post_meta( $id, 'Email', false )[0],
        get_the_content()
    );
    fputcsv( $output_handle, $row);
endwhile;
if(!have_posts()){fputcsv( $output_handle, ['No Support Requests']);}
 
// Close output file stream
fclose( $output_handle );

Finally, I want to use WP Cron to set a monthly cron job to send out the email with the CSV attached.  To change the interval you’ll need to change the “‘interval’ => 2592000,”  and “$DownloadReportFrom = date(‘m_d_Y’,strtotime(“-1 month”));” to be the time span you want. Also, you’ll definaly need to adjust the to and from emails within the support_monthly_report function. This is the code to go in the functions.php file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//Support Question reports
function my_cron_schedules($schedules){
    if(!isset($schedules["monthly"])){
        $schedules["monthly"] = array(
            'interval' => 2592000,
            'display' => __('Once a month'));
    }
    return $schedules;
}
add_filter('cron_schedules','my_cron_schedules');
 
add_action('init','support_monthly_report_schedule');
add_action('support_monthly_report_job','support_monthly_report');
 
function support_monthly_report_schedule(){
    if (!wp_next_scheduled ( 'support_monthly_report_job' )) {
        wp_schedule_event(time(), 'monthly', 'support_monthly_report_job');
    }
}
 
function support_monthly_report(){
    $email = 'TOEMAIL';
    $subject = 'Monthly Report';
    $message = 'EMAILBODY';
 
    $DownloadReportFrom = date('m_d_Y',strtotime("-1 month"));
    $DownloadReportTo = date('m_d_Y',strtotime("now"));
    $output_filename = 'Support_report-' . $DownloadReportFrom .'-'. $DownloadReportTo  . '.csv';
 
    include('generate_customer_report_excel.php');
    usleep(300000);
    $attachments = array( WP_CONTENT_DIR . '/uploads/support_requests/'.$output_filename );
    $headers = 'From: FROMNAME ' . "\r\n";
 
    wp_mail($email,$subject, $message, $headers, $attachments );
}

And that should do it! You’ll be sending snazzy spreadsheets out in no time 😀

Permalink » No comments

Compress images with simple smusher

I wrote a similar post using TinyPNG about a week ago, but the 500 image limit was a bit small so I built my own very simple image compressor.  So I’ve updated the code to work with my simple smusher, added a few improvements, and I have a PHP version as well.

Ruby Version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
require 'open-uri'
require "json"
require "net/http"
require "uri"
require 'rubygems'
 
start = (ARGV[0] ? ARGV[0] : 0).to_i
last = start+100
i = 0
total_savings = 0
Dir.glob("public/system/rich/rich_files/rich_files/**/*.{jpg,png}") do |item|
  #only do 100 images at a time
  break if i == last
 
  #ignorre directories and original files
  next if item == '.' or item == '..' or item.include? "original"
  i = i + 1
  next if i <= start
 
  url = item.sub 'public', 'http://www.jimstoppani.com'
  uri = URI.parse("http://yourdomain.com/?i=#{url}")
  http = Net::HTTP.new(uri.host, uri.port)
  http.read_timeout = 500
  request = Net::HTTP::Get.new(uri.request_uri)
  response = http.request(request)
 
  if response.code == "200"
    result = JSON.parse(response.body)
  else
    break
  end
 
  puts "Converted: #{item}"
 
  if result[0]['dest_size'] < result[0]['src_size']
    open(item, 'wb') do |file|
      file << open(result[0]["dest"]).read
    end
    puts "Original: #{result[0]['src_size']} Optimized #{result[0]['dest_size']}"
    total_savings = total_savings + (result[0]['src_size']-result[0]['dest_size'])
  else
    puts "no size difference"
  end
end
 
puts "All done! Total Savings: #{total_savings}"

This one has a few advantages over the last version – it needs a variable sent with it so it doesn’t run into the problem of re-compressing images that have already been compressed.  It does 100 images at once, so you could run it in a loop like “ruby tinify.rb 0″, “ruby tinify.rb 100″, “ruby tinify.rb 200″ ect.

It also checks the response from the compressor, and it there’s no file size savings it doesn’t copy over the new version. And once it’s done, it will tell you the total savings.

PHP Version

HTML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Photo Smusher</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
 
    <script>
        $(function(){
        var total = 0;
        var start = 0;
 
 
        function smush_it() {
            var jqxhr = $.getJSON("smush.php?img=FOLDER&p="+start, function(data) {
              total = total + parseFloat(data[0].total);
              start = start+5;
              $("#total").text(total);
              $("#start").text(start);
              if(start < 5010) {
                  smush = setTimeout(function(){smush_it()},60000);
              }
            }).fail(function( jqxhr, textStatus, error ) {
                var err = start + ' ' + textStatus + ", " + error;
                smush = setTimeout(function () {
                    smush_it()
                }, 60000);
                $("#error").text(err);
            });
        }
 
        $("#total").text(total);
        $("#start").text(start);
        smush_it();
 
        });
    </script>
</head>
 
<body>
    <p>Progress: <span id="start"></span></p>
    <p>KB saved: <span id="total"></span></p>
    <p id="error"></p>
</body>
</html>

PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
function loadFile($url) {
    $ch = curl_init();
 
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $url);
 
    $data = curl_exec($ch);
    curl_close($ch);
 
    return $data;
}
 
 
function downloadfile($file, $path, $fn) {
    if(isset($file) && isset($path) && isset($fn)) {
        $fc = implode('', file($file));
 
        $mode = (file_exists($path . $fn) ? 'w': 'x+');
        $Files = fopen($path . $fn, $mode);
 
        if ((fwrite($Files, $fc)) != 0){
            fclose($Files);
        } else{
            echo 'Error.';
        }
    }
}
 
if(isset($_REQUEST['img']) && isset($_REQUEST['p']) && is_numeric($_REQUEST['p'])) {
   $total = 0;
   $limit = 5;
   $start = $_REQUEST['p'];
   $end = $start + $limit;
   $item = 0;
 
   $handle = opendir('../images/'.$_REQUEST['img']);
   while ($file = readdir($handle)){
      if($item >= $start && $item <= $end) {
         $extension = strtolower(substr(strrchr($file, '.'), 1));
         if($extension == 'jpg'){
            $image = json_decode(loadFile('http://yourdomain.com/?i=http://yourdomain.com/images/'.$_REQUEST['img'].'/'.$file));
 
            $change = $image[0]->src_size-$image[0]->dest_size;
 
            if($change > 1) {
               $total += $change;
               downloadfile($image[0]->dest,'../images/'.$_REQUEST['img'].'/',$file);
            }
         } 
      } elseif($item > $end) {
         break;
      }
      $item++;
   } 
 
   echo '[{"total":"'.$total.'"}]';
}
 
?>

The HTML page goes through images in the FOLDER you designate, 5 at a time until you hit image 50010, which you can customize based on your folder size. It sends an ajax request to the php file, then reports back the total KB saved.  It will run the script once a minute 60000, which you can change depending on your server’s performance.

Once the PHP page receives a request it makes sure it has all the required request variables, sets up the counters, then goes through all the images in the designated folder.

The compressor will handle php and gif files, but I only had jpgs in this project so I limited it to only process jpgs.  So once it find a jpg it will send the full URL to your image compression service, you can replace “yourdomain.com” with the url of your service.  Then it decodes the json, and if there was any compression in the file size it will download the new image over the old one.  And once it’s gone through 5 images will report the total KB saved back to the HTML file.

 

Permalink » No comments

WordPress – Custom length excerpt

If you ever need a custom length excerpt in your wordpress site, and want to leave the excerpt length alone for the rest of the site, you can use wp_trim_words on the content to pull out exactly how many words you want the excerpt to be.

In the following example I made a 10 words excerpt, but you can make it any length you want by changing the number.

<?php echo wp_trim_words(get_the_content(), 10); ?>

Permalink » No comments

Omit posts with a custom field from WordPress

It took me a while to track this down so I thought I’d post about it.

In a recent project I had the need to omit posts from a WordPress query based on if they had a custom field set to them.

It’s pretty simple once you know how it’s done, imaging you want the ability to remove posts from the homepage by hand.  If you used the custom field name of “hide-me” your query would look like this:

1
query_posts('meta_key=hide-me&meta_compare=NOT EXISTS&meta_value=test');

For “meta_key=hide-me” the hide-me can be whatever custom field you like, and for “meta_value=test” it doesn’t matter at all what you set for “test.”  That’s a little odd, but WordPress needs to have that value, but since you’re only checking weather or not the custom field is there the value doesn’t matter.

The tricky bit it is “meta_compare=NOT EXISTS”  and took a bit of digging to find that.  I had to dig through the documentation of WP_Query to track it down.  It also says that it won’t work for query_posts, but that bit might be out of date, at least for WordPress 3.7.1.  That basically just tell WordPress to check if that custom field doesn’t exist.

It’s pretty easy to use, but I wasn’t able to find a simple write-up on it anywhere and took me a bit of trial and error to get going.  So I thought I’d share :)

Permalink » No comments