This article continues off of the last one for setting up a single WordPress install for multiple sites. Last time I went through the wp-config.php file. The next step is my newsite.php shell script. Just like last time, I am going to break up the shell script into pieces.
#!/usr/bin/php
<?php
$dev = "example.com"; # Development Domain
$maxlength = 6; # Maximum Client/Site ID Length
$wwwroot = "/path/to/client/sites"; # Base root for all client sites
$wpuser = "wordpress_user"; # MySQL user that WordPress uses
$dbauthdomain = "localhost"; # The authorized domain for WordPress MySQL account
$corepath = "/path/to/wordpress/"; # Path to WordPress core
$links = array(
"index.php",
"wp-blog-header.php",
"wp-cron.php",
"wp-mail.php",
"license.txt",
"wp-comments-post.php",
"wp-includes",
"wp-settings.php",
"readme.html",
"wp-config.php",
"wp-links-opml.php",
"wp-signup.php",
"wp-activate.php",
"wp-load.php",
"wp-trackback.php",
"wp-admin",
"wp-login.php",
"xmlrpc.php",
"wp-content/index.php",
"wp-content/plugins",
"wp-content/themes/twentytwelve"); # Array of files to create symbolic links for
This is all the variables for the script and are subjected to customization depending on the particular server environment. I think most of the variables are self-explanatory, but if I am wrong, feel free to put questions in the comments section below. There is one that I would like to highlight, the $dev variable. This is to be set on the domain name that you use for your development environment. Some people like to just have it as a subdomain of the main site (e.g. dev.livesite.com), but this was developed for a company that previously had their development environment off of a disorganized directory structure off of a single domain (e.g. previewsite.com/long/confusing/path/to/site) and I was making it to where each customer’s site is a subdomain of the development domain (e.g. client-site.previewsite.com). After setting it up, I liked this structure so much that I started using it for my personal projects. It allows me to start working before domain propagation, DNS propagation, and before the client gives me live access.
# -- Get input until unique ones are entered -->
do {
# -- Get client ID until a vaild one is entered -->
do {
fwrite(STDOUT, "Enter the client id: ");
$c = trim(fgets (STDIN));
if (strlen($c) >= $maxlength || preg_replace("/[a-z0-9]/", "", $c))
fwrite(STDOUT, "Error: Must be ".$maxlength." characters or less, only lowercase and numeric.\n");
} while (strlen($c) >= $maxlength || preg_replace("/[a-z0-9]/", "", $c));
# -- Get site ID until a valid one is entered -->
do {
fwrite (STDOUT, "Enter the site id: ");
$s = trim (fgets(STDIN));
if (strlen($s) >= $maxlength || preg_replace("/[a-z0-9]/", "", $s))
fwrite(STDOUT, "Error: Must be ".$maxlength." characters or less, only lowercase and numeric.\n");
} while (strlen($s) >= $maxlength || preg_replace("/[a-z0-9]/", "", $s));
if (file_exists("/var/www/clients/".substr($c, 0, 1)."/".$c."/".$s."/dev"))
fwrite(STDOUT, "Error: Site ".$c."-".$s.".".$dev." already exists.\n");
} while (file_exists("/var/www/clients/".substr($c, 0, 1)."/".$c."/".$s."/dev"));
This is getting the input from the user of the application to identify the new site. It works off of a client ID and a site ID and makes sure that they are both valid and not currently existing. I added a six character limit after getting ID’s from the Vice President that were close to 20 characters in length. No one wants to try to direct the customer over the phone to the development site at verylongclientid-evenlongersiteidblahblahblah.example.com as opposed to jsmith-store.example.com. So I told the VP that it would not allow more than six characters and refused to admit that it was a self-imposed limitation.
# -- Connect to registry and add new entry in site registry -->
require "regconnect.php";
mysql_query("
INSERT INTO
apache_vhosts (
host,
path,
virt,
spiders,
dbschema)
VALUES (
'".$c."-".$s.".".$dev."',
'clients/".substr($c, 0, 1)."/".$c."/".$s."/dev',
1,
0,
'cl_".$c."_".$s."_dev');") or die("Failure in INSERT query.\n");
# -- Create site's schema and give WordPress access -->
mysql_query("
CREATE DATABASE
cl_".$c."_".$s."_dev;") or die("Failure in CREATE query.\n");
mysql_query("
GRANT
SELECT,
INSERT,
UPDATE,
DELETE,
CREATE,
DROP,
INDEX,
ALTER
ON
cl_".$c."_".$s."_dev.*
TO
'".$wpuser."'@'".$dbauthdomain."';") or die("Failure in GRANT query.\n");
This connects into the database, creates the development database, and grant the proper permissions to the WordPress user account on this new database.
# -- Create directories and symbolic links for new WordPress site -->
$hostpath = "/var/www/clients/".substr($c, 0, 1)."/".$c."/".$s."/dev/";
$hostpathx = explode("/", $hostpath);
for ($i = 0; $i < count($hostpathx); $i++) {
$curdir = "";
for ($j = $i; $j > -1; $j--) $curdir = $hostpathx[$j]."/".$curdir;
if (!file_exists($curdir)) shell_exec("mkdir ".$curdir);
}
shell_exec("mkdir ".$hostpath."/wp-content");
shell_exec("mkdir ".$hostpath."/wp-content/themes");
foreach ($links as $link) shell_exec("ln -s ".$corepath.$link." ".$hostpath.$link);
mysql_close();
fwrite(STDOUT, "WordPress install complete at ".$hostpath."\n");
?>
To end, I cycle through creating directories and symbolic links to the WordPress core. I end it all with the message telling the user it was successful. After running this script, you can then go to the development site address and see the typical initial WordPress page. Note: the initial user information entered into this page doesn’t matter because with the wp-config.php file, it will get deleted and replaced with the global users since it is in the reserved block for the global users.
The next file is launchsite.php. This is the shell script for launching the site from development to live.
#!/usr/bin/php
<?php
$dev = "example.com"; # Development Domain
$maxlength = 6; # Maximum Client/Site ID Length
$wwwroot = "/path/to/client/sites/"; # Base root for all client sites
$dblocation = "localhost"; # IP or domain name for the MySQL server
$wpuser = "wordpress_user"; # MySQL user that WordPress uses
$dbauthdomain = "localhost"; # The authorized domain for WordPress MySQL account
$wppass = "password"; # MySQL password that WordPress uses
Just like the last script, I start with the variables to customize it for the particular server environment that it is in.
# -- Get input until existing ones are entered -->
do {
# -- Get client ID until a valid one is entered -->
do {
fwrite(STDOUT, "Enter the client id: ");
$c = trim(fgets (STDIN));
if (strlen($c) > $maxlength || preg_replace("/[a-z0-9]/", "", $c))
fwrite(STDOUT, "Error: Must be ".$maxlength." characters or less, only lowercase and numeric.\n");
} while (strlen ($c) > $maxlength || preg_replace("/[a-z0-9]/", "", $c));
# -- Get site ID until a valid one is entered -->
do {
fwrite(STDOUT, "Enter the site id: ");
$s = trim(fgets(STDIN));
if (strlen($s) > $maxlength || preg_replace("/[a-z0-9]/", "", $s))
fwrite(STDOUT, "Error: Must be ".$maxlength." characters or less, only lowercase and numeric.\n");
} while (strlen($s) > $maxlength || preg_replace("/[a-z0-9]/", "", $s));
if (!file_exists($wwwroot.substr($c, 0, 1)."/".$c."/".$s."/dev/"))
fwrite(STDOUT, "Error: Cannot Locate Site for ".$c."-".$s.".".$dev."\n");
} while (!file_exists($wwwroot.substr($c, 0, 1)."/".$c."/".$s."/dev/"));
fwrite(STDOUT, "Enter the live domain name: http://");
$livehost = trim(fgets(STDIN));
$devdb = "cl_".$c."_".$s."_dev";
$livedb = "cl_".$c."_".$s."_live";
$devpath = $wwwroot.substr($c,0,1)."/".$c."/".$s."/dev";
$livepath = $wwwroot.substr($c,0,1)."/".$c."/".$s."/live";
$devhost = $c."-".$s.".".$dev;
Just like with the last script, I use do {} while (); loops to get proper input from the user to figure out which site it is. I didn’t put any validation on the live domain name since about the only thing I could catch was invalid characters and I figured most mistakes would be typos that would have to come to me for manual fixing in the Site Registry. After all the input was collected, I created variables for the environment.
require "regconnect.php";
mysql_query("
DELETE FROM
apache_vhosts
WHERE
host='".$livehost."';");
mysql_query("
INSERT INTO
apache_vhosts (
host,
path,
virt,
dbschema)
VALUES (
'".$livehost."',
'".$livepath."',
1,
'".$livedb."');") or die("Failure in INSERT (registry) query.\n");
mysql_query("
CREATE DATABASE IF NOT EXISTS
".$livedb.";") or die("Failure in CREATE DATABASE query.\n");
mysql_query("
GRANT
SELECT,
INSERT,
UPDATE,
DELETE,
CREATE,
DROP,
INDEX,
ALTER
ON
".$livedb.".*
TO
'".$wpuser."'@'".$dbauthdomain."';") or die("Failure in GRANT query.\n");
mysql_close();
This is the database part that works off of the site registry MySQL account. It inserts the live domain information into the site registry, creates the live site database, and grants access to the new database to the WordPress database account.
mysql_connect($dblocation, $wpuser, $wppass);
mysql_select_db($devdb);
$result = mysql_query("SHOW TABLES;");
$tables = array();
while ($row = mysql_fetch_array($result)) $tables[] = $row[0];
mysql_select_db($livedb);
for ($i = 0; $i < count($tables); $i++) {
mysql_query("
DROP TABLE IF EXISTS
".$livedb.".".$tables[$i].";");
mysql_query("
CREATE TABLE
".$livedb.".".$tables[$i]."
LIKE
".$devdb.".".$tables[$i].";") or die("Failure in CREATE TABLE query.\n");
mysql_query("
INSERT INTO
".$livedb.".".$tables[$i]."
SELECT
*
FROM
".$devdb.".".$tables[$i].";") or die("Failure in INSERT (site) query.\n");
}
Now we connect with the WordPress user account and duplicate the database from development. It creates an array of all tables with the SHOW TABLES query. Then it cycles through the array creating the identical tables and inserts the identical data.
mysql_query("
UPDATE
".$c."_options
SET
option_value='http://".$livehost."'
WHERE
option_value='http://".$devhost."';") or
fwrite(STDOUT, "Error in updating domain name in live database.\n");
mysql_query("
UPDATE
".$c."_options
SET
option_value='1'
WHERE
option_name='blog_public';") or
fwrite(STDOUT, "Error in updating to public site in live database.\n");
if (!file_exists($livepath)) shell_exec("mkdir ".$livepath);
shell_exec("rsync -av ".$devpath."/ ".$livepath);
shell_exec("chgrp -R users ".$livepath);
shell_exec("chmod -R 775 ".$livepath);
fwrite(STDOUT, "WordPress Site has been launched for http://".$livehost.".\n");
?>
To finish this off, we fix the few WordPress options that need to change like the domain name and whether or not it is a public site. After that, we do an rsync of the file system and make sure the permissions are set properly.
The last script is updatedevsite.php and it is very similar to the last one. It updates the development site from the live data. This way all the customer changes to the content would not be lost when we ended up doing further development or designing on their site after launch. I am not going to go step by step since it really is the same as the last one, but in reverse direction copying live to dev instead of dev to live.
#!/usr/bin/php
<?php
$dev = "example.com"; # Development Domain
$maxlength = 6; # Maximum Client/Site ID Length
$wwwroot = "/path/to/client/sites/"; # Base root for all client sites
$dblocation = "localhost"; # IP or domain name for the MySQL server
$wpuser = "wordpress_user"; # MySQL user that WordPress uses
$dbauthdomain = "localhost"; # The authorized domain for WordPress MySQL account
$wppass = "password"; # MySQL password that WordPress uses
# -- Get input until existing ones are entered -->
do {
# -- Get client ID until a valid one is entered -->
do {
fwrite(STDOUT, "Enter the client id: ");
$c = trim(fgets (STDIN));
if (strlen($c) > $maxlength || preg_replace("/[a-z0-9]/", "", $c))
fwrite(STDOUT, "Error: Must be ".$maxlength." characters or less, only lowercase and numeric.\n");
} while (strlen ($c) > $maxlength || preg_replace("/[a-z0-9]/", "", $c));
# -- Get site ID until a valid one is entered -->
do {
fwrite(STDOUT, "Enter the site id: ");
$s = trim(fgets(STDIN));
if (strlen($s) > $maxlength || preg_replace("/[a-z0-9]/", "", $s))
fwrite(STDOUT, "Error: Must be ".$maxlength." characters or less, only lowercase and numeric.\n");
} while (strlen($s) > $maxlength || preg_replace("/[a-z0-9]/", "", $s));
if (!file_exists($wwwroot.substr($c, 0, 1)."/".$c."/".$s."/live/"))
fwrite(STDOUT, "Error: Cannot Locate Site for ".$c."-".$s."."\n");
} while (!file_exists($wwwroot.substr($c, 0, 1)."/".$c."/".$s."/live/"));
$devdb = "cl_".$c."_".$s."_dev";
$livedb = "cl_".$c."_".$s."_live";
$devpath = $wwwroot.substr($c,0,1)."/".$c."/".$s."/dev";
$livepath = $wwwroot.substr($c,0,1)."/".$c."/".$s."/live";
$devhost = $c."-".$s.".".$dev;
require "regconnect.php";
if ($r = mysql_query("
SELECT
host
FROM
apache_vhosts
WHERE
path='".$livepath."';")) $livehost = mysql_fetch_array($r)[0];
mysql_close();
mysql_connect("localhost", $wpuser, $wppass);
mysql_select_db($livedb);
$result = mysql_query("SHOW TABLES;");
$tables = array();
while ($row = mysql_fetch_array($result)) $tables[] = $row[0];
mysql_select_db($devdb);
for ($i = 0; $i < count($tables); $i++) {
mysql_query("
DROP TABLE IF EXISTS
".$devdb.".".$tables[$i].";");
mysql_query("
CREATE TABLE
".$devdb.".".$tables[$i]."
LIKE
".$livedb.".".$tables[$i].";") or die("Failure in CREATE TABLE query.\n");
mysql_query("
INSERT INTO
".$devdb.".".$tables[$i]."
SELECT
*
FROM
".$livedb.".".$tables[$i].";") or die("Failure in INSERT query.\n");
}
mysql_query("
UPDATE
".$c."_options
SET
option_value='http://".$devhost."'
WHERE
option_value='http://".$livehost."';") or
fwrite(STDOUT, "Error in updating domain name in dev database.\n");
mysql_query("
UPDATE
".$c."_options
SET
option_value='0'
WHERE
option_name='blog_public';") or
fwrite(STDOUT, "Error in updating to private site in dev database.\n");
shell_exec("rsync -av ".$livepath."/ ".$devpath);
shell_exec("chgrp -R users ".$devpath);
shell_exec("chmod -R 775 ".$devpath);
fwrite(STDOUT, "WordPress Site has been launched for http://".$devhost.".\n");
?>
That’s it. Scripts for creating new development site, launching to live, and updating from live. I ended up teaching my designers to use PuTTY and run these themselves. Ultimately though, I was wanting to integrate into the system where on the development site, there was buttons to launch and update from live that asked for username and password and did it for them right then and there. I also had an extra call in these script that I left out here, and that was where it logged the launches and updates in our database for tracking work. In our weekly meetings, these logs were used to track the progress on projects and save us time in discussing it.
Leave a Reply