Changing a wordpress domain name

Holy permanent domain names Batman!

Once in a while, I discover something about wordpress that makes me wonder how it ever got so popular. The most recent discovery is that wordpress stores your site’s domain name into the mysql database. Worst of all, it doesn’t just store it in one place but litters it throughout several tables and records. And of course wordpress does not come with any way to migrate the site to a different domain name.

This is a problem because I want to be able to setup a pre-production site on a pre-production domain and then move it to a production domain once it’s ready.  For example, I recently wanted move this blog to wordpress 3.2 but I wanted to see it in action on my test url first.

After doing some research, there were no official list of where the domain name may be stored and there were just too many tables and records to look through manually.  In addition, I knew I had to change the domain a few times so I put in some time and wrote a php script to look through all the tables, records, fields, etc for the existing domain name.  When it finds one in a particular field, it updates the value to the new domain.

<?php

$longopts = array(
  "db_name:",
  "db_user:",
  "db_pw:",
  "find_string:",
  "replace_string:",
  "replace_guid_field"
);

$options = getopt("", $longopts);
print "Found arguments:\n";
print_r($options);

$database = $options["db_name"];
$database_user = $options["db_user"];
$database_pw = $options["db_pw"];
$find_string = $options["find_string"];
$replace_string = $options["replace_string"];
$replace_guid = array_key_exists("replace_guid_field", $options);

$link = mysql_connect("localhost", $database_user, $database_pw);
mysql_select_db($database);

$query = "SHOW TABLES FROM $database";
$result = mysql_query($query);

while ($arr = mysql_fetch_array($result, MYSQL_NUM)) {
  foreach ($arr as $table) {
    process_table($table, $find_string, $replace_string, $replace_guid);
  }
}
mysql_close($link);

print "done";

function process_table($table, $find_string, $replace_string, $replace_guid) {
  print "\nprocessing $table....";

  $field_arr = array();
  $query = "SELECT * FROM $table";
  $result = mysql_query($query);
  while ($row_arr = mysql_fetch_array($result, MYSQL_ASSOC)) {
    foreach ($row_arr as $key => $value) {
      if (strpos($value, $find_string) !== false) {
        // Create a list of fields which contain the found string.
        $field_arr[$key] = true;
        //print_r($row_arr);
      }
    }
  }

  if (count($field_arr) == 0) {
    print " string not found.";
  } else {
    print " string found in field(s): " . implode(', ', array_keys($field_arr)) . "\n";
    foreach (array_keys($field_arr) as $field) {
      if ($field != "guid") {
        $query = "UPDATE $table SET $field = replace($field, '$find_string', '$replace_string') WHERE $field LIKE '%$find_string%'";
        update_table($query);
      } else {
        if ($replace_guid) {
          $query = "UPDATE $table SET $field = replace($field, '$find_string', '$replace_string') WHERE $field LIKE '%$find_string%'";
          update_table($query);
        } else {
          print "\nString found in guid field but not replacing. Override this by using the --replace_guid_field flag.\n";
        }
      }
    }
  }
}

function update_table($query) {
  print "\n$query\n";
  $result = mysql_query($query);
  $num_updated = mysql_affected_rows();
  print "... updated $num_updated records\n";
}
?>

The script is designed to be run from the command line.  Run the script by passing in the following options:

php change-domain.php --db_name=<your_db_name> --db_user=<db_login> --db_pw=<db_password> --find_string=<original_domain> --replace_string=<new_domain> --replace_guid_field

You can download the script but use at your own risk. If you do use it, I suggest you backup your database first.

Your php may not have the command line interpreter installed.  You’ll need the php5-cli package in order to run php from the shell.

apt-get install php5-cli
>> dpkg --list |grep php
ii  libapache2-mod-php5              5.3.2-1ubuntu4.9                  server-side, HTML-embedded scripting languag
ii  php5-cli                         5.3.2-1ubuntu4.9                  command-line interpreter for the php5 script
ii  php5-common                      5.3.2-1ubuntu4.9                  Common files for packages built from the php
ii  php5-gd                          5.3.2-1ubuntu4.9                  GD module for php5
ii  php5-mysql                       5.3.2-1ubuntu4.9                  MySQL module for php5

 

Remove url / website from wordpress comments

Holy spam bots Batman!

I finally got fed up with comment spam on my wordpress blog.  To help solve my problem, I decided to remove all the the url / website values for comments in my blog.  It seems like the most popular use of the author url value in a comment is for spam.  In fact, I would say over 90% of all my comments containing an author url is some form of ad spam. For these cases, the author url usually links to some random site with no relevant content. On the other hand, almost all my readers who post legitimate comments do not bother putting in a comment author url.

So I decided the author url is useless and decided take aggresive action against it:

  1. remove the url field from the comment form
  2. prevent comment posts with author url in it
  3. remove urls for existing comments
  4. remove comments which contained random author urls which has a slight hint of being spam.

Customize the comment form

To remove the url or website field from the comment form, you can customize the wordpress comment form by adding a filter and using the “comment_form_default_fields” hook. Go into your theme and find all usages of the function comment_form().  Before each usage, add the following filter:

    <?php
      function remove_url_field($fields) {
        unset($fields['url']);
        return $fields;
      }
      add_filter('comment_form_default_fields', 'remove_url_field')
    ?>

    <?php comment_form(); ?>

I’m using the default twenty-eleven theme and the above appears in comments.php.

Validate server comment post

Customizing the comment form is great for your users but it will not make a bit of difference for spam bots.  Spam bots can post comments directly to your wordpress and bypass the form directly.  Because of this, you need to add validation to the php file that accepts comments.  Inside wp-comments-post.php, find the following lines of code (should be somewhere near the middle of the file):

$comment_author       = ( isset($_POST['author']) )  ? trim(strip_tags($_POST['author'])) : null;
$comment_author_email = ( isset($_POST['email']) )   ? trim($_POST['email']) : null;
$comment_author_url   = ( isset($_POST['url']) )     ? trim($_POST['url']) : null;
$comment_content      = ( isset($_POST['comment']) ) ? trim($_POST['comment']) : null;

Put the following if statement right after:

if ($comment_author_url) {
   wp_redirect('/');
   exit;
}

This will redirect anyone who submits a comment author url back to the main page.  This should only affect bots which bypassed your form since it will not realize you do not have a url input anymore.

Remove existing author url

Finally, remove the existing urls from the mysql database.  The urls are stored in wp_comments in the comment_author_url field.

mysql> update wp_comments set comment_author_url = '';

Notes

The above changes were performed on wordpress 3.2.1.

WordPress: No Page comments

It seems like the “Allow Comments” option on the “Add new page” screen does not work in wordpress.  Specifically, it does not work for the default theme Kubrick.

Allow comments option

Allow comments option

Seeing this option, it’s easy to assume this enables comments on your wordpress Pages, especially for the default theme. But unfortunately, this particular option in the admin page has no affect on the default Kubrick theme. To enable comments on your Page(s), you have to select a theme which includes them or modify your default Kubrick theme.

Default theme with page comments

To change your default theme, add the following line to /wp-content/themes/default/page.php

<?php
/**
 * @package WordPress
 * @subpackage Default_Theme
 */

get_header(); ?>

    <div id="content" class="narrowcolumn">

        <?php if (have_posts()) : while (have_posts()) : the_post(); ?>
        <div class="post" id="post-<?php the_ID(); ?>">
        <h2><?php the_title(); ?></h2>
            <div class="entry">
                <?php the_content('<p class="serif">Read the rest of this page &raquo;</p>'); ?>

                <?php wp_link_pages(array('before' => '<p><strong>Pages:</strong> ', 'after' => '</p>', 'next_or_number' => 'number')); ?>

            </div>
        </div>
        <?php endwhile; endif; ?>
    <?php edit_post_link('Edit this entry.', '<p>', '</p>'); ?>

    <?php comments_template(); ?>
    </div>

<?php get_footer(); ?>

Additional References

http://wordpress.org/support/topic/246879

http://wordpress.org/support/topic/196788