HTML Content to a Save As / Open Dialog

I had a project that required outputting an HTML content so that the Open/Save As dialog box would be shown.  This is what I did; I post it here for my own reference (and for anyone else that comes along). The code block below is as a function, where the $post_output is the entire HTML content you want to use with the Open/Save As dialog.

// --------------------------------------------------------------------------------
// output the file with a prompt to save
function blogtohtml_output_file($post_output = "nothing found")
$thefile = 'blogexport_' . date('m-d-Y_his') . '.html' ;
if ( headers_sent())
throw new Exception('Uh-oh...headers already sent. 🙁 ') ;
$size = strlen($post_output) ;
header('Content-Description: File Transfer') ;
header('Content-Type: application/octet-stream') ;
header('Content-Disposition: attachment; filename=' . $thefile) ;
header('Content-Transfer-Encoding: binary') ;
header('Connection: Keep-Alive') ;
header('Expires: 0') ;
header('Cache-Control: must-revalidate, post-check=0, pre-check=0') ;
header('Pragma: public') ;
header('Content-Length: ' . $size) ;
ob_clean() ;
flush() ;
echo $post_output ;
exit ;
return ;

The code sets up a filename that will be used with the Save As choice. If headers are already sent, an  error is shown (you can’t have any output to the screen when you use the Header Content commands.

Note that the last four lines are required in order to use this function within other code.

Works quite well, so it’s here to be available the next time I need it.

Recovering from a Hacked WordPress Site

(…another in a series of posts mostly for myself – so I’ll have a place to remember things….but maybe the random visitor will find something useful here.)

One of the sites I visit is a Q&A forum type place about WordPress development. And there is always a question about ‘how do I fix a hack of my WordPress site?’.

If you ask the googles, you’ll find lots of answers; some are actually good answers. Some aren’t. So I thought I’d put down what I would do if I was in that situation. (I’ve had to do this before, for other sites and clients.)

Step 1: Secure Access

If your WP site gets hacked, someone got into the site somehow. Doesn’t really matter how they did. So my first step is to secure access to the site.

This means that I will first change the password on my hosting place. Strong passwords, of course, and one that I have never used before (or will use elsewhere).

Next, I look at the external access to my files. This means looking at all FTP user accounts, and changing the ones that I use to develop or access the site. (Or in the case of client sites, their access.). And looking for user accounts that I don’t recognize. Those will get immediately deleted. Then I will create a brand-new FTP user account, again with a strong password. All other FTP accounts will get deleted.

Then I look at the credentials for the WP database. I’ll change the password for that database. If the site is well-visited, I might create a new database user, assign it to that database. The access rights for that database will be limited; nobody gets all of the possible database rights.

Since I have changed (or added) the database credentials, I’ll go into the WP config file where those credentials are stored and change it to the new account. A quick check then to ensure that the site still works. If it is OK, I’ll delete the old database user account.

Just for grins, I’ll also change the passwords for any email accounts on that site. That is probably not needed, but couldn’t hurt. I’ll just have to remember to notify those email accounts of the change. Most of my sites are single-user (me), so that’s usually not a big deal

Step 2: Update everything

Next, I’ll log into the admin area of the site. I’ll create a new admin-level user, give it a strong password (of course), and then log out and log in with that new user account. Once logged in, I’ll delete the old admin account.

While in the user accounts area, I’ll create a user called ‘admin’. Strong password, but that account gets the lowest privileges. (Some hack attempts like to try to use the user called ‘admin’. So, I let them try.)

Next, I’ll look at any other user accounts. If there are several or more, I’ll screenshot the list, and then reduce their access levels to the lowest privileges. I’ll fix that access later when things are cleaned up.

Now, into the Admin, Update screen and reinstall the latest version of WP.

The next step is a bit more time-consuming – deleting and reinstalling all themes/plugins. Not just installing, delete everything first, then reinstall as needed. Hopefully, I’ve remembered to keep the number of active (and inactive) themes and plugins to the minimum. (Usually a good idea not to have plugins that aren’t active. Just delete them.)

To reinstall all plugins, I have to delete them first. Many may have some complex settings screens, so screenshots of those are done (and printouts if needed). Once I’ve documented all plugin settings, I’ll deactivate and then delete them all – using the option (if presented) to delete the files associated with the plugins.

This may break the site, of course, or at least not make it work like it normally does. So I may do this during low-activity times. (Some of my sites are always low-activity, so I just ‘do it’.)

After all plugins are fully removed, I start reinstalling the ones that are needed, using my reference screenshots/printouts to re-set the options for each as needed. I only use plugins from the WP repository, even the ones that I write. That way, I make sure that I get the most current version of the plugins – even though I check and update plugins (and themes) daily.

Once I have finished with all of the plugins, I do the same for themes. Screenshot the settings, delete the themes (files included), then reinstall from the WP Theme Depository. Put back all the settings (from my screenshots) as needed.

In both cases, I may use my FTP client to look at the theme and plugins folders to make sure that they have actually been deleted. Then I’ll reinstall.

A check of the site to make sure it works OK completes this step.

Step 3 – Investigate

Now I have (with any luck) a clean WP install, with current (and clean) themes and plugins.  I need to make sure that anything left over is not going to cause a problem.

First, I check the htaccess file. I know what that file should look like (look at the WP Codex for help), so I make sure that the file is how it is supposed to be.

Next, I use my favorite FTP program (WinSCP) to log into the site – after changing the user/pass to the new one. I then poke around all of the site folders for files that I don’t recognize. I usually sort by date (oldest first), and look for files that are earlier than today. Remember that I reinstalled everything, so everything that is valid should have today’s date stamp. I don’t rely on that date sort, though, I look at all of the files; the date sort is just the first pass through all the files.

And I look at all site folders, not just the root folder. Rouge files will jump out because of their name or datestamp. I sometimes move those rouge files into a non-site folder in case I want to look at them later. As I move files, I make sure the site is till working properly.

Step 4 – Product (ecommerce) and other files

I take a look at all of the media files on the site, on the off-chance that one of them is compromised. But usually those aren’t a problem.

What might be a problem is ecommerce/product files. It may be that a product file is the compromised access point. I’ll look at every single product, every single field for that product, to make sure that all is well. That can be a bit time-consuming, but so can a re-infection if a product is the infection point.

Step 5 – Posts and Pages and Comments

It may be that a post or page has some rouge code in it. Now, by default, WP will not execute code inside a post/page. But, on the off-chance it does, I want to ensure that all posts/pages are OK. Depending on the site, I may use a SQL command or two to look for indicates of code inside a page/post. If it is a smaller site, I’ll just look at all posts/pages. I may sort by date, if I am aware of the approximate date that the hack got into my site.

It’s not likely that a Comment will be the infection point, since I do have some protection against spammers – via my own plugins  (FormSpammerTrap for Comments) and Akismet. But a quick look at Comments is a good idea. I set up the anti-spam features to immediately discard all potential spam, so usually don’t have a problem with that.

Step 6 – Final Cleanup

By this point, the site should be back to normal. I’ve been checking the site as I go through each step, looking for indications of continued infection.

If I had to reduce privileges on user accounts, I put those back to normal.  And I make sure that backups are in place and working. Not just the database, but all files (WP, themes, and plugins).

Winding Up

The above steps are a really good start at cleaning up a site. Some would argue that a ‘Nuke from Orbit’ – starting from scratch by deleting everything, but that may not be an option for most sites. My procedure takes a bit of time (many hours – more than 8, usually), so it’s not easy. (And, if I am doing it for a client, it’s not inexpensive.)

But, I’ve been successful in site cleanups before with this process.

What do you think? Add your ‘thinky bits’ in the comments.

Changing Contact Form 7 with the wpcf7_before_send_mail Hook

Some notes about using (and perhaps abusing) the Contact Form 7 process in a WordPress site. (Contact Form 7 is a popular plugin for WordPress sites that lets you easily create a Contact Us type form. We use it here, although we’ve also tweaked what it does with our FormSpammerTrap for Contact Form 7 plugin that effectively reduces Contact Form spam. You can check out that plugin – and our other FormSpammerTrap spam-bot blocking techniques at our site.)

We were inspired by an article we found via the googles while searching for information on CF7 hooks, because we wanted to do things to the CF7 form content after it was submitted, but before the form was emailed. The article is here. The code is theirs, but we wanted to explain it in more detail (mostly for our own purposes, as we wanted to use a similar process for our own purposes).

Let’s take a look at what we can do after the Contact form is submitted, and before CF7 emails the message. Our intent is to change the Subject line of the form to something other than what the visitor entered. You could easily use this code for your own purposes.

First, lets ‘hook’ into the wpcf7_before_send_mail process. This is done with the standard WP add_action function. (Remember that you can ‘hook’ into any WP function that has a ‘hook’. Ask the googles how the add_action thing works.) Here is the code that you would place in your Child Theme’s function.php file (or perhaps in a plugin):

The add_action code
add_action( 'wpcf7_before_send_mail', 'my_change_subject_mail' );

This will wait for the hook to be called, and run the my_change_subject_mail() function.

Now we need to create the my_change_subject_mail function. I’m going to show and explain each line separately. We’ll put the entire function at the end.

This defines the function:

Defining the function
function my_change_subject_mail($WPCF7_ContactForm)

This will get the current WPCF7_ContactForm object. We need this object to change the subject value therein.

Get the current WPCF7_ContactForm object
$wpcf7 = WPCF7_ContactForm :: get_current() ;

Here we get the Submission object, which is generated when the user hits the ‘send’ submit button.

Cet the WPCF7 Submission Object Instance
$submission = WPCF7_Submission :: get_instance() ;

Next, let’s ensure that the contact form has been submitted.

Check if there is a submission of the contact form
if ($submission)

If the form is empty, exit the function.

Exit the function if it the contact form is empty
if ( empty ($posted_data))
return ;

This is how we read a value of the form. The basic CF7 Contact Form has variables, this next line references the variable that was defined as ‘your-message’. If you want to work with another field on the form, use that field name as the parameter of the $posted_data array.


Read the your-subject field contents into the $subject variable
$subject = $posted_data['your-message'];

At this point, you could do a search/replace on the subject, or anything else. For instance, maybe you’d like to store the message fields in a database. You would set various variables – like we did with the $subject variable – to values from the $posted_data array. Then you could store those variables into a table in your database.

We’re not going to do anything with the $subject variable, we just wanted to show you that it could be done.

Our intent is to change the ‘subject’ of the email to something else. First, we have to read the property of the $mail object. This is the object that CF7 uses to create the email.

Get the contact form's values into the $mail array
// do some replacements in the cf7 email body
$mail = $WPCF7_ContactForm->prop('mail') ;

Since the subject line in our CF7 form is called ‘subject’, here is how we change the contents of that field:


Change the Subject field's value
$mail['subject'] = "this is an alternate subject" ;

Now that we have changed a value in the $mail array, we need to set that value in the WPCF7_ContactForm object:

Store our new subject back into the CF7 ContactForm object
// Save the email body
$WPCF7_ContactForm->set_properties( array("mail" => $mail)) ;

If we are curious about all of the contents of the WPCF7_ContactForm object, we could quickly write it to the error.log file. This is OK for our purposes, as we are on a development site, and the error.log is a quick way to look at something. Note that you can’t ‘echo’ or ‘print’ anything to the screen in this process; it won’t be shown.

Look at all the contents of the object
// error_log( print_r( $WPCF7_ContactForm, 1 ) );

All done. We’ll return the object just in case it is needed.

Exit out of the function
return $WPCF7_ContactForm ;

By looking at the contents of the object, you might find other things that you might like to change. The $mail part of the object is stuff that will be emailed.

The result of all of this is that we have changed the subject line of the emailed message with out little function. We could add additional parts (maybe some extra data like the IP address of the sender, or whatever) to the $mail array -maybe the message content –  and that would also be sent.

All done!  Any questions, use the comments. We’ll try our best to muddle through an answer to your question.

Here’s the entire function:

The Entire Code
add_action( 'wpcf7_before_send_mail', 'my_change_subject_mail' );
function my_change_subject_mail($WPCF7_ContactForm)
$wpcf7 = WPCF7_ContactForm :: get_current() ;
$submission = WPCF7_Submission :: get_instance() ;
if ($submission)
$posted_data = $submission->get_posted_data() ;
// nothing's here... do nothing...
if ( empty ($posted_data))
return ;
$subject = $posted_data['your-message'];
//$subject = substr($this->replace_tags( $template['subject'] ), 0, 50);
// do some replacements in the cf7 email body
$mail = $WPCF7_ContactForm->prop('mail') ;
$mail['subject'] = "this is an alternate subject" ;
// Save the email body
$WPCF7_ContactForm->set_properties( array("mail" => $mail)) ;
// error_log( print_r( $WPCF7_ContactForm, 1 ) );
// return current cf7 instance
return $WPCF7_ContactForm ;

PHP Error Handling

This is one of a (hopefully) continuing series of posts on programming, mostly PHP/MySQL and a bit of JavaScript. It’s mostly for my own use – since this is not a widely-read blog. But it may be useful for anyone that stumbles into this place.

This short post is about error handling in PHP. You’ll find lots of other info on error handling, how to do it, what it is, etc. But here’s the code that I use. It’s simple, and will catch production-type errors, where you don’t want people to see the error on your nice web site.

I’m not going to explain every line of the code, you can ask the googles if you need help on anything. But this works for me. If there is a severe error, one that would cause cryptic error message on the web site, this will catch the error and redirect the user back to your main page. It will also email error information to you. It’s very simplistic, but it will get the job done. Add information unique to your site as you need.

Simple Error Handling
  1. // simple error trapping. Change the email address as needed for your site.
  2. // Copyright 2017 by Rick Hellewell and ( LLC) and All Rights Reserved.</code>
  3. // Shared via CCO license.
  4. set_error_handler("myErrorHandler");
  5. function myErrorHandler($errno, $errstr, $errfile, $errline) {
  6. switch ($errno) {
  7. case E_NOTICE :
  8. case E_USER_NOTICE :
  9. $errors = "Notice" ;
  10. return ;
  11. break ;
  12. case E_WARNING :
  13. case E_USER_WARNING :
  14. $errors = "Warning" ;
  15. break ;
  16. case E_ERROR :
  17. case E_USER_ERROR :
  18. $errors = "Fatal Error" ;
  19. break ;
  20. default :
  21. $errors = "Unknown" ;
  22. break ;
  23. }
  24. $xmsg = sprintf("PHP %s: %s in %s on line %d", $errors, $errstr, $errfile, $errline);
  25. xerror_log( $xmsg) ;
  26. mail(', 'Web Site Program Error', $xmsg);
  27. header("Location: index.php") ;
  28. die() ;
  29. return true ;
  30. }

And that’s it. Add it to your ‘includes’ page (the code included on all parts of your site), after setting your email address, and changing the Subject in the mail() command.

URL Smashing

We run a few WordPress sites – some for us, and some for others. One of the things that we noticed on a couple of sites was that long URLs were visually irritating, at least to us. They seem to get in the way of the content, and sometimes ‘bleed over’ the content areas.

So we went looking for a solution – a plugin. And all the ones we found were not simple enough. They required you to enter special codes, or other irritating things. We wanted one that worked automatically. And we couldn’t find one that we liked.

The result – our second WordPress plugin – “URL Smasher”. It uses the shortening service, so requires a Google API account, but those are free and easy to get. Once you set it up by adding your Google API key, and checking two boxes, any content that is saved – posts, pages, or comments – that have URLs get them automatically shortened.

It works quite well. Like the URL for the previous post. I entered the actual URL as text: ,  and as a link. Each is automagically shortened when I save or publish the post.

You’ll find the plugin here at (also shortened).

We are quite impressed with ourselves.

Design Changes

I spent a bit of time making a few minor changes to this place. Among them, a new logo up there.

There was also some tweaking of the ‘responsive’ styling of this place. A bit of new CSS code here and there.

Not that I have any special graphic skills. But the ‘look’ is a bit cleaner than before, I think.

Database Cleanup for Security

Several of my web sites use custom databases. Some of those web sites are gone (on purpose). But the databases were still there.

So I spent a bit of time deleting some unused databases and database users. It’s a security thing: there might be some personal information on some of the databases, and deleting unused data is a ‘good thing’.

Database security is important. Here are a few things to think about:

  • Do you have unused databases anywhere?
  • Is there public/personal information in the data tables?
  • Have you secured the user rights to those databases — not giving full access to a user out of convenience?
  • Do you have backup copies of the databases?
  • Are databases that contain personal information encrypted?

Any other considerations? Let me know in the comments.

Domain Responsibility

Another site that I look at often had a complaint from a reader about losing their domain name when it expired and wasn’t renewed. The reader said that they didn’t get the renewal notices, the automatic renewal didn’t work, and they had to pay quite a bit of money to get it back.

When a domain expires, it reverts back to the registrar (in this case GoDaddy, but this is common practice). The registrar can then do what they want with the domain name, often making it available for anyone else at a premium cost. If the original owner wants to get it back, it will cost much more than the original domain cost. Again, all of this is common practice among just about any domain registrar.

The owner claimed that GoDaddy didn’t notify them of the expiration. The Security Dawg has just about all of our domains registered through GoDaddy. That puts me on their mailing list. I get several emails a month from GoDaddy about their latest promotions, in addition to renewal notices.

I am the owner of record for those domains. You are required to have a valid email address (and other contact information) for all domains you own. And, once a year (or more often), you will get a notice from the domain registrar about verifying your contact information.

The reader claimed that they didn’t get any of those notices. I find that difficult to believe, when I get multiple emails a month from GoDaddy. I suspect that those notification emails were either ignored, or got routed to the person’s spam folder.

The reader claimed that they had the domain set up for auto-renewal, using an on-file credit card. If the renewal didn’t work, as when the credit card expired or was invalid, the registrar would have sent emails about that. That has happened to me: I have let a few domain names purposely expire, and I get multiple notices for renewal along with notices about expiration.

So the reader was quite incensed when they realized their web site with that domain name was no longer working. No more email (all email went through the same domain name). And then they had to pay a big premium to get the domain back.

They thought that was quite unfair.

The Security Dawg disagrees. If you are the owner a domain name (for any reason), then you have a responsbility to protect that domain name.

  • You need to make sure that all contact information is proper for the domain name
  • You need to make sure that the email addresses associated with that domain name work properly.
  • You need to ensure that the billing information (credit card number) is current.
  • You need to ensure that you get emails from the registrar – that they don’t get into your spam folder.

If the domain name is important to your business or for personal use, then you have a responsibility to ensure that you properly manage that domain name. This applies to domain name registrars, web site hosting companies, your web site code (do you have backup copies of your web site?) the whole works.

If you fail to be responsible, then you can’t complain when your domain name goes away.

The domain name belongs to you (and maybe your business). Treat it like any other valuable asset.

If you own or manage a domain, you might consider verifying that all your contact information is current. And keep an eye on expiration dates. This applies to web site hosting. And backups – you should be able to reconstruct your web site if something goes wrong (that’s another post).

We’re Mobile Friendly

Google is implementing a search ranking protocol this week that will penalize web sites that are not mobile-friendly. If a site is not ‘responsive’ (able to adjust to varying screen widths and devices to keep the screen readable), then it’s search results ranking will be demoted in favor of responsive site.

The Google have been warning about this for a couple of months, and have provided guidance to web developer dweebs on how to make sites responsive.

This page will allow anyone to test the responsiveness of any site. And the page also provides the guidance and information on how to make a responsive site.

But many sites might see their search rankings (and therefore traffic to their site) get demoted for visitors using a mobile device for searching. Note that a desktop search will not result in affected ranking, although I suspect in the future.

I do a lot of web sites. This site passed the test: it shows that the page is “mobile-friendly”

But anyone that has a web site that relies on search traffic (and revenue) will see reduced search visitors because of this new ranking algorithm for mobile searches. They will need to scramble to get into a responsive mode.

FormSpammerTrap for Comments WordPress Plugin

My new WordPress plugin to block form spammers/bots is now publicly visible at . It blocks comment spam from ‘bots’ with a simple technique. It doesn’t have captchas, hidden fields, silly questions, or other things that don’t work. It just looks for ‘human activity’ on the comment form, and if a ‘bot’ tries to submit a comment, they immediately get sent to my FormSpammerTrap web site.

It uses the same techniques that I use for comment forms (more info about that here and here and here ), but now it is a WordPress plugin, so it is quite easy to install and configure. And it is quite effective…I’ve never gotten any ‘bot’ spam on any site that I have installed it.

The whole plugin coding process was interesting, and a good learning process. I’ve already got some enhancements in mind for newer versions. But I was quite proud of myself for getting this one to work…and that it is now available among the millions of other WordPress plugins.