Customizing the WordPress Install Process with install.php

I came across a super interesting post at WPBits about [automating the wordpress installation][1] process.  Some of the parts about where some of the functions are located in WP that allow you to change certain things on the installation are outdated since the post is from 2007, but the concept remains perfectly intact.  If you find yourself installing a lot of WordPress blogs and are tired of always having to delete the “This is your First Post” post, changing the permalinks etc. then you can take advantage of this little trick.

Trolling the Source Code Like a Boss

If you are cruising through source (because there is plenty of stuff that isn’t well documented yet in the Codex) you will see

/** Include user install customize script. */ 
if ( file_exists(WP_CONTENT_DIR . '/install.php') ) require (WP_CONTENT_DIR . '/install.php');

/wp-admin/includes/upgrade.php.  Now this is interesting because it means that when WordPress installs (or upgrades) it will look for a file called install.php in your /wp-content folder… or whatever you are using instead of the default /wp-content folder.  This file doesn’t exist by default but it gives you a chance to run some functions of your own during the installation process (since  /wp-admin/includes/upgrade.php is required by /wp-admin/install.php)

My First Custom Install.php

I followed WPBit’s example and decided to show off my new power over WordPress by putting the following into my custom /wp-content/install.php file:

No Bacon for You <br/>Evil Superpowers <?php exit();?> 

is obviously isn’t helpful, but it is a good proof of concept and let’s you know that you have all the power.  Of course…

With great power comes great responsibility. – Uncle Ben

Functions We Can Hijack for Our Own Purposes

As WPBits explains in more detail, some of the ideal to tweak would be:

  • wp_install()
  • wp_install_defaults()
  • wp_new_blog_notification()
  • wp_upgrade()

All of these functions are now (currently version 3.3.1) in /wp-admin/includes/upgrade.php and they are pluggable, meaning they come with an if(function_exists()) wrapper that lets you define your own versions to override their behavior. Since wp_install_defaults() is the culprit for creating those useless “first post” posts and pages, we’re going to target it.  I copied the entire function to my own file: /wp-content/install.php and then hacked out all the parts that inserted the first post, first page, and changed the default category from Uncategorized to General.  I even cut down the number of widgets that are activated by default. I also wanted to change some options, like permalinks, time zone, size of the post editor, banning emoticons, etc.  So I added a bunch of update_option() commands.  Just because I thought it’d be super clever, I updated the ping list, moderate and blacklist comment terms from text files by using file_get_contents().

Automagically Creating Myself as an Admin

I usually install WP for someone else, I decided that I wanted to create my client’s account on install and automatically create my own admin account.  I might reverse this process as I don’t like how the install’s welcome email doesn’t include a link to the login screen.  I know that you just tack on wp-login.php but a newbie client might not.  At the same time, the way I have it means I never have to change my credentials in the file… which I prefer for now.

Anyway, I initially overrode the default wp_install() file to do this, by repeating the process of copying the function from /wp-admin/includes/upgrade.php to my own install.php, but it turns out that I can just create a new user from the function I already have.  In that case, I decided to only override one core function. Here’s the code I used.  Remember to use it at your own risk.  This is really targeted at people who know what they are doing with WordPress and are installing it on a regular basis.  

Take note to change the username and email address for the account you are creating for yourself… currently they are left as USERNAME and YOU@YOUREMAIL.COM.  Right now it generates a random password, but that could easily be changed to always assign yourself the same password.

Edited: Sept 9, 2014 from WordPress 4.0

function wp_install_defaults( $user_id ) {
    global $wpdb, $wp_rewrite, $table_prefix;

    // Default category
    $cat_name = __( 'General' );
    /* translators: Default category slug */
    $cat_slug = sanitize_title( _x( 'General', 'Default category slug' ) );

    if ( global_terms_enabled() ) {
        $cat_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM {$wpdb->sitecategories} WHERE category_nicename = %s", $cat_slug ) );
        if ( $cat_id == null ) {
            $wpdb->insert( $wpdb->sitecategories, array('cat_ID' => 0, 'cat_name' => $cat_name, 'category_nicename' => $cat_slug, 'last_updated' => current_time('mysql', true)) );
            $cat_id = $wpdb->insert_id;
        update_option(  'default_category', $cat_id  );
    } else {
        $cat_id = 1;

    $wpdb->insert( $wpdb->terms, array('term_id' => $cat_id, 'name' => $cat_name, 'slug' => $cat_slug, 'term_group' => 0) );
    $wpdb->insert( $wpdb->term_taxonomy, array('term_id' => $cat_id, 'taxonomy' => 'category', 'description' => '', 'parent' => 0, 'count' => 1));
    $cat_tt_id = $wpdb->insert_id;

    // Set up default widgets for default theme.
    update_option( 'widget_search', array ( 2 => array ( 'title' => '' ), '_multiwidget' => 1 ) );
    update_option( 'sidebars_widgets', array ( 'wp_inactive_widgets' => array (), 'sidebar-1' => array ( 0 => 'search-2' ), 'sidebar-2' => array (), 'sidebar-3' => array (), 'array_version' => 3 ) );

    if ( ! is_multisite() )
        update_user_meta( $user_id, 'show_welcome_panel', 1 );
    elseif ( ! is_super_admin( $user_id ) && ! metadata_exists( 'user', $user_id, 'show_welcome_panel' ) )
        update_user_meta( $user_id, 'show_welcome_panel', 2 );

    if ( is_multisite() ) {
        // Flush rules to pick up the new page.

        $user = new WP_User($user_id);
        $wpdb->update( $wpdb->options, array('option_value' => $user->user_email), array('option_name' => 'admin_email') );

        // Remove all perms except for the login user.
        $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->usermeta WHERE user_id != %d AND meta_key = %s", $user_id, $table_prefix.'user_level') );
        $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->usermeta WHERE user_id != %d AND meta_key = %s", $user_id, $table_prefix.'capabilities') );

        // Delete any caps that snuck into the previously active blog. (Hardcoded to blog 1 for now.) TODO: Get previous_blog_id.
        if ( !is_super_admin( $user_id ) && $user_id != 1 )
            $wpdb->delete( $wpdb->usermeta, array( 'user_id' => $user_id , 'meta_key' => $wpdb->base_prefix.'1_capabilities' ) );

    /* * BEGIN KIA ADDITIONS * Customize Some Options */ 

    // Set Timezone 
    $timezone = "America/Chicago"; 
    update_option( 'timezone_string', $timezone ); 

    // Start of the Week 
    // 0 is Sunday, 1 is Monday and so on 
    update_option( 'start_of_week', 0 ); 

    // Disable Smilies 
    update_option( 'use_smilies', 0 ); 

    // Increase the Size of the Post Editor 
    update_option( 'default_post_edit_rows', 40 ); 

    // Update Ping Services 
    $services = file_exists( WP_CONTENT_DIR . '/KIA-ping-list.txt' ) ? file_get_contents( 'KIA-ping-list.txt', true ) : ''; 
    update_option('ping_sites',$services); } 

    // Update Comment Moderation List 
    $modlist = file_exists( WP_CONTENT_DIR . '/KIA-comment-moderation-list.txt' ) ? file_get_contents( 'KIA-comment-moderation-list.txt', true ) : ''; 
    update_option( 'moderation_keys', $modlist ); } 

    // Update Comment Blacklist 
    $blacklist = file_exists( WP_CONTENT_DIR . '/KIA-comment-blacklist.txt') ? file_get_contents( 'KIA-comment-blacklist.txt', true ) : ''; 
    update_option( 'blacklist_keys', $blacklist ); } 

    // Don't Organize Uploads by Date 

    // Update Permalinks 
    update_option( 'selection','custom' ); 
    update_option( 'permalink_structure','/%post_id%/%postname%/' ); 

    // Create Self as Admin User. 
    // props @David for wp_insert_user() tip 
    $userdata = array(
        'user_login' => 'USERNAME',
        'user_pass' => wp_generate_password(),
        'user_email' => 'YOU@YOUREMAIL.COM',
        'first_name' => 'USER',
        'last_name' => 'NAME',
        'nickname' => 'User',
        'display_name' => 'User',
        'user_url' => '',
        'role' => 'administrator' 

    $self_id = username_exists( $username ); 

    if ( ! $self_id ) { 
        $self_id = wp_insert_user( $userdata );
        update_user_option( $self_id, 'default_password_nag', true, true ); 
        wp_new_user_notification( $self_id, $userdata['password'] ); 


I don’t keep this file live on the server, but I suppose you could if you dropped an .htaccess file into the /wp-content folder and banned access to your custom install.php file

<Files install.php> Order Allow,Deny Deny from all </Files> 

That about sums it all up.  Let me know what cool jedi tricks you are doing with your custom install.php file!


Download the whole thing from my Customizing the WordPress install gist.

This entry was posted in Tutorial and tagged , , . Bookmark the permalink. Trackbacks are closed, but you can post a comment.