// &database=Database Name;string;example &live_prefix=Table prefix for live site;string;modx_ &staging_prefix=Table prefix for staging site;string;modxstaging_ &live_path=Path to live site, relative to shared parent;string;/var/www/sites/default/htdocs/example/ &staging_path=Path to staging site, relative to shared parent;string;/var/www/sites/default/htdocs/example_staging/ &rm_path=Location of rm binary;string;/bin/rm &rsync_path=Location of rsync binary;string;/usr/bin/rsync // // modx_staging // Author: John Susek // Email: [email protected] $modx_staging_version = "0.21"; $tables_to_deploy = array( "$staging_prefix"."active_users" => 1, "$staging_prefix"."categories" => 1, "$staging_prefix"."documentgroup_names" => 1, "$staging_prefix"."document_groups" => 1, "$staging_prefix"."keyword_xref" => 1, "$staging_prefix"."membergroup_access" => 1, "$staging_prefix"."membergroup_names" => 1, "$staging_prefix"."member_groups" => 1, "$staging_prefix"."site_content" => 1, "$staging_prefix"."site_content_metatags" => 1, "$staging_prefix"."site_htmlsnippets" => 1, "$staging_prefix"."site_keywords" => 1, "$staging_prefix"."site_metatags" => 1, "$staging_prefix"."site_modules" => 1, "$staging_prefix"."site_module_access" => 1, "$staging_prefix"."site_module_depobj" => 1, "$staging_prefix"."site_plugins" => 1, "$staging_prefix"."site_plugin_events" => 1, "$staging_prefix"."site_snippets" => 1, "$staging_prefix"."site_templates" => 1, "$staging_prefix"."site_tmplvars" => 1, "$staging_prefix"."site_tmplvar_access" => 1, "$staging_prefix"."site_tmplvar_contentvalues" => 1, "$staging_prefix"."site_tmplvar_templates" => 1, "$staging_prefix"."system_eventnames" => 1, "$staging_prefix"."system_settings" => 1, "$staging_prefix"."webgroup_access" => 1, "$staging_prefix"."webgroup_names" => 1, "$staging_prefix"."web_groups" => 1, "$staging_prefix"."web_users" => 1, "$staging_prefix"."web_user_attributes" => 1, "$staging_prefix"."web_user_settings" => 1, "$staging_prefix"."manager_log" => 1, "$staging_prefix"."manager_users" => 1 ); $tables_to_reverse_deploy = array( "$live_prefix"."event_log" => 1, "$live_prefix"."jot_content" => 1, "$live_prefix"."jot_subscriptions" => 1, "$live_prefix"."user_attributes" => 1, "$live_prefix"."user_messages" => 1, "$live_prefix"."user_roles" => 1, "$live_prefix"."user_settings" => 1 ); print "<style>table { border-collapse:collapse;border:1px solid gray;margin:13px;} td { border:1px solid gray;padding:4px; } h2, h3, h4 { color:#264F17; margin:5px 15px;font-family: sans-serif; } .shell { margin:15px;padding:5px;background:black; color:white; border:2px solid gray; } p { margin:15px; } .gray { color:gray;margin:0 15px; } .success { padding:5px;background-color:rgb(143,199,12);color:#264F17; } .success2 { padding:5px;color:#264F17; }</style>"; if (!$_POST["deploy"]) { // Display the UI print "<FORM action='".$_SERVER['REQUEST_URI']."' method='post'> <h2>MODx Deploy</h2> <br/><h3>Would you like to deploy to the live site?</h3>"; print "<p class='success'> <INPUT type='hidden' name='deploy' value='deploy'> <INPUT type='submit' value='Deploy'> </p></FORM>"; print "<br/><h3>Configuration overview</h3><pre><table border='0' style='border:0'> <tr><td>Database</td><td></td><td>$database</td></tr> <tr><td>Table prefix</td><td>Staging</td><td>$staging_prefix</td></tr> <tr><td></td><td>Live</td><td>$live_prefix</td></tr> <tr><td>Deploy</td><td>From Staging</td><td>$staging_path</td></tr> <tr><td></td><td>To Live</td><td>$live_path</td></tr></p> <tr><td>Tables to deploy</td><td></td><td><pre>"; print_r($tables_to_deploy); print "</pre></td></tr><tr><td>Tables to reverse deploy</td><td></td><td><pre>"; print_r($tables_to_reverse_deploy); print "</pre></td></tr></table></pre>"; } else { // Do the table copying print "<h2>MODx Deploy</h2><pre>"; $rsync_cmd = "$rsync -a --exclude=assets/cache --exclude=config.inc.php $staging_path $live_path;"; print "<h3 title='$rsync_cmd'>Syncing filesystems...</h3><div class='shell'>"; print shell_exec("$rsync_cmd"); print "<b>done.</b></div>"; print "<h3>Clearing caches...</h3><div class='shell'>"; print shell_exec("$rm -f $live_path/assets/cache/*pageCache.php"); print shell_exec("$rm -f $staging_path/assets/cache/*pageCache.php"); print "<b>done.</b></div>"; $sql = "SHOW TABLES FROM $database"; print "<h3 title='$sql'>Getting list of tables from database '$database'...</h3><div class='shell'>"; $results = $modx->db->query($sql); print "<b>done.</b></div>"; print "<h3>Syncing tables...</h3>"; print "<table>"; while( $row = $modx->db->getRow($results) ) { $table = $row["Tables_in_$database"]; if ( preg_match("/$staging_prefix/", $table) ) // if this table is a staging table.. { if ($tables_to_deploy["$table"]) { $counterpart = str_replace("$staging_prefix", "$live_prefix", $table); print "<tr><td>$table</td><td>---></td><td>$counterpart</td>"; $sql_drop = "DROP TABLE IF EXISTS `$counterpart`"; if (!$modx->db->query($sql_drop)) { print "Error dropping live table! SQL statement used: <br/>$sql_drop<br/>"; } $sql_create = "CREATE TABLE `$counterpart` LIKE `$table`"; if (!$modx->db->query($sql_create)) { print "Error creating new live table from staging table! SQL statement used: <br/>$sql_create<br/>"; } $sql_populate = "INSERT INTO `$counterpart` SELECT * FROM `$table`"; if ($modx->db->query($sql_populate)) { print "<td class='success2' title='$sql_drop ; $sql_create ; $sql_populate'>✔</td></tr>"; } else { print "Error populating new live table from staging table. SQL statement used: <br/>$sql_populate<br/>"; } } } elseif ( preg_match("/$live_prefix/", $table) ) // if a live table.. { if ($tables_to_reverse_deploy["$table"]) { $counterpart = str_replace("$live_prefix", "$staging_prefix", $table); print "<tr><td>$counterpart</td><td><---</td><td>$table</td>"; $sql_drop = "DROP TABLE IF EXISTS `$counterpart`"; if (!$modx->db->query($sql_drop)) { print "Error dropping staging table: <br/>$sql_drop<br/>"; } $sql_create = "CREATE TABLE `$counterpart` LIKE `$table`"; if (!$modx->db->query($sql_create)) { print "Error creating new staging table from live table! SQL statement used: <br/>$sql_create<br/>"; } $sql_populate = "INSERT INTO `$counterpart` SELECT * FROM `$table`"; if ($modx->db->query($sql_populate)) { print "<td class='success2' title='$sql_drop ; $sql_create ; $sql_populate'>✔</td></tr>"; } else { print "Error creating new staging table from live table. SQL statement used:<br/>$sql_populate<br/>"; } } } else // else another table without either prefix, dont touch { print "<br/>Other table (ignoring): $table<br/>"; } } print "</table>"; print "<h3 class='success2'>✔ Site deployed successfully.</h3>"; print "</p></pre>"; }
For module configuration, use this string: &database=Database Name;string;example &live_prefix=Table prefix for live site;string;modx_ &staging_prefix=Table prefix for staging site;string;modxstaging_ &live_path=Path to live site, relative to shared parent;string;/var/www/sites/default/htdocs/example/ &staging_path=Path to staging site, relative to shared parent;string;/var/www/sites/default/htdocs/example_staging/ &rm_path=Location of rm binary;string;/bin/rm &rsync_path=Location of rsync binary;string;/usr/bin/rsync Changelog ----- 0.21 FIXED: Indexes and restraints were not getting duplicated with the tables. 0.2 Use configuration variables 0.1 Initial release


1. Install two modx 0.9.6.3 installations to the same web server. These two installs are referred to as the live-site and the staging-site. 2. Point them both to the same SQL server. Use identical settings for both of them, but give each installation a unique table prefix (I like modx_ for live and modxstaging_ for staging). 3. Log into staging-site manager. Copy and paste modx_staging 0.1 source code into a new module. Modify variables at the top of the source code to match your installation. Name the module 'Deploy to Live Site'. 4. Now, when you use the module, you will see a Deploy button. Click it and it will copy content from the staging-site to the live-site. 5. To tweak what tables get moved between the staging-site and live-site, modify the $tables_to_deploy variable in the source.
// modx_staging // Author: John Susek // Email: [email protected] // License: CC Attribution 2.5 $modx_staging_version = "0.1"; $modx_staging_database = 'example'; $staging_prefix = 'modxstaging_'; $prefix = 'modx_'; $sites_path = '/var/www/sites/default/htdocs/'; $live_path = 'example/'; $staging_path = 'example_staging/'; $rsync = "/usr/bin/rsync"; $rm = "/bin/rm"; $tables_to_deploy = array( "$staging_prefix"."active_users" => 1, "$staging_prefix"."categories" => 1, "$staging_prefix"."documentgroup_names" => 1, "$staging_prefix"."document_groups" => 1, "$staging_prefix"."keyword_xref" => 1, "$staging_prefix"."membergroup_access" => 1, "$staging_prefix"."membergroup_names" => 1, "$staging_prefix"."member_groups" => 1, "$staging_prefix"."site_content" => 1, "$staging_prefix"."site_content_metatags" => 1, "$staging_prefix"."site_htmlsnippets" => 1, "$staging_prefix"."site_keywords" => 1, "$staging_prefix"."site_metatags" => 1, "$staging_prefix"."site_modules" => 1, "$staging_prefix"."site_module_access" => 1, "$staging_prefix"."site_module_depobj" => 1, "$staging_prefix"."site_plugins" => 1, "$staging_prefix"."site_plugin_events" => 1, "$staging_prefix"."site_snippets" => 1, "$staging_prefix"."site_templates" => 1, "$staging_prefix"."site_tmplvars" => 1, "$staging_prefix"."site_tmplvar_access" => 1, "$staging_prefix"."site_tmplvar_contentvalues" => 1, "$staging_prefix"."site_tmplvar_templates" => 1, "$staging_prefix"."system_eventnames" => 1, "$staging_prefix"."system_settings" => 1, "$staging_prefix"."webgroup_access" => 1, "$staging_prefix"."webgroup_names" => 1, "$staging_prefix"."web_groups" => 1, "$staging_prefix"."web_users" => 1, "$staging_prefix"."web_user_attributes" => 1, "$staging_prefix"."web_user_settings" => 1, "$staging_prefix"."manager_log" => 1, "$staging_prefix"."manager_users" => 1 ); $tables_to_reverse_deploy = array( "$prefix"."event_log" => 1, "$prefix"."jot_content" => 1, "$prefix"."jot_subscriptions" => 1, "$prefix"."user_attributes" => 1, "$prefix"."user_messages" => 1, "$prefix"."user_roles" => 1, "$prefix"."user_settings" => 1 ); print "<style>table { border-collapse:collapse;border:1px solid gray;margin:13px;} td { border:1px solid gray;padding:4px; } h2, h3, h4 { color:#264F17; margin:5px 15px;font-family: sans-serif; } .shell { margin:15px;padding:5px;background:black; color:white; border:2px solid gray; } p { margin:15px; } .gray { color:gray;margin:0 15px; } .success { padding:5px;background-color:rgb(143,199,12);color:#264F17; } .success2 { padding:5px;color:#264F17; }</style>"; if (!$_POST["deploy"]) { print "<FORM action='".$_SERVER['REQUEST_URI']."' method='post'> <h2>MODx Deploy</h2> <br/><h3>Would you like to deploy to the live site?</h3>"; print "<p class='success'> <INPUT type='hidden' name='deploy' value='deploy'> <INPUT type='submit' value='Deploy'> </p></FORM>"; print "<br/><h3>Configuration overview</h3><pre><table border='0' style='border:0'> <tr><td>Database</td><td></td><td>$modx_staging_database</td></tr> <tr><td>Table prefix</td><td>Staging</td><td>$staging_prefix</td></tr> <tr><td></td><td>Live</td><td>$prefix</td></tr> <tr><td>Deploy</td><td>From Staging</td><td>$sites_path$staging_path</td></tr> <tr><td></td><td>To Live</td><td>$sites_path$live_path</td></tr></p> <tr><td>Tables to deploy</td><td></td><td><pre>"; print_r($tables_to_deploy); print "</pre></td></tr><tr><td>Tables to reverse deploy</td><td></td><td><pre>"; print_r($tables_to_reverse_deploy); print "</pre></td></tr></table></pre>"; } else { print "<h2>MODx Deploy</h2><pre>"; $rsync_cmd = "cd $sites_path; $rsync -a --exclude=assets/cache --exclude=config.inc.php $staging_path $live_path;"; print "<h3 title='$rsync_cmd'>Syncing filesystems...</h3><div class='shell'>"; print shell_exec("$rsync_cmd"); print "<b>done.</b></div>"; print "<h3>Clearing caches...</h3><div class='shell'>"; print shell_exec("$rm $sites_path/$live_path/assets/cache/*pageCache.php"); print shell_exec("$rm $sites_path/$staging_path/assets/cache/*pageCache.php"); print "<b>done.</b></div>"; $sql = "SHOW TABLES FROM $modx_staging_database"; print "<h3 title='$sql'>Getting list of tables from database '$modx_staging_database'...</h3><div class='shell'>"; $results = $modx->db->query($sql); print "<b>done.</b></div>"; print "<h3>Syncing tables...</h3>"; print "<table>"; while( $row = $modx->db->getRow($results) ) { $table = $row["Tables_in_$modx_staging_database"]; if ( preg_match("/$staging_prefix/", $table) )// if this table is a staging table.. { if ($tables_to_deploy["$table"]) { $counterpart = str_replace("$staging_prefix", "$prefix", $table); print "<tr><td>$table</td><td>---></td><td>$counterpart</td>"; $sqld = "DROP TABLE IF EXISTS `$counterpart`"; if (!$modx->db->query($sqld)) { print "Error dropping live table:<br/>$sqld<br/>"; } $sql = "CREATE TABLE IF NOT EXISTS `$counterpart` SELECT * FROM `$table`"; if ($modx->db->query($sql)) { print "<td class='success2' title='$sqld ; $sql'>✔</td></tr>"; } else { print "Error creating new live table from staging table. SQL statement used:<br/>$sql<br/>"; } } } elseif ( preg_match("/$prefix/", $table) ) // if a live table.. { if ($tables_to_reverse_deploy["$table"]) { $counterpart = str_replace("$prefix", "$staging_prefix", $table); print "<tr><td>$counterpart</td><td><---</td><td>$table</td>"; $sqld = "DROP TABLE IF EXISTS `$counterpart`"; if (!$modx->db->query($sqld)) { print "Error dropping staging table:<br/>$sqld<br/>"; } $sql = "CREATE TABLE IF NOT EXISTS `$counterpart` SELECT * FROM `$table`"; if ($modx->db->query($sql)) { print "<td class='success2' title='$sqld ; $sql'>✔</td></tr>"; } else { print "Error creating new staging table from live table. SQL statement used:<br/>$sql<br/>"; } } } else // else another table without either prefix, dont touch { print "<br/>Other table (ignoring): $table<br/>"; } } print "</table>"; print "<h3 class='success2'>✔ Site deployed successfully.</h3>"; print "</p></pre>"; }
Okay, I’ve created a module to get a little practice with modx and php.
This will synchronize two modx installations, both at the filesystem level and the database table level. You can choose which tables get copied to the live site. I’ve written up some notes along with the module; could someone try this and let me know if they find it useful? I can clean it up a bit, or add features, if there is interest.
I’m calling it modx_staging for reference. Please read the notes and try to get it working with two newly linked installs! Copy and paste contents of staging.txt to a new module (I’ve also included source code in this post). Name the module "Deploy to Live Site" or something like that.
I will need to check this out with some attention, however, in looking at the code I already have one question. There are a few snippets that create custom tables (maxigallery and jot for example). How would we add more tables to be included in the transfer?
I like this, thanksI will need to check this out with some attention, however, in looking at the code I already have one question. There are a few snippets that create custom tables (maxigallery and jot for example). How would we add more tables to be included in the transfer?
"$staging_prefix"."your_table_name" => 1,
"$prefix"."your_table_name" => 1,