PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/DeployMintAbstract.php

https://github.com/acobrotsen/DeployMint
PHP | 1670 lines | 758 code | 88 blank | 824 comment | 66 complexity | 51a2031ce44098d771af723c7333f4dc MD5 | raw file
Possible License(s): GPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. abstract class DeployMintAbstract implements DeployMintInterface
  3. {
  4. const PAGE_INDEX = 'deploymint';
  5. const PAGE_BLOGS = 'deploymint/blogs';
  6. const PAGE_PROJECTS = 'deploymint/projects';
  7. const PAGE_REVERT = 'deploymint/revert';
  8. const PAGE_OPTIONS = 'deploymint/options';
  9. const PAGE_HELP = 'deploymint/help';
  10. const DP_TABLE_ALL = 'table_all';
  11. const DP_DIR_UPLOADS = 'dir_uploads';
  12. protected $wpTables = array('commentmeta', 'comments', 'links', 'options', 'postmeta', 'posts', 'term_relationships', 'term_taxonomy', 'terms', 'users', 'usermeta');
  13. protected $pdb;
  14. protected $defaultOptions = array(
  15. 'git' => '',
  16. 'mysql' => '',
  17. 'mysqldump' => '',
  18. 'rsync' => '',
  19. 'numBackups' => 5,
  20. 'datadir' => '',
  21. 'preserveBlogName' => 1,
  22. 'backupDisabled' => 0,
  23. 'temporaryDatabase' => '',
  24. 'backupDatabase' => '',
  25. );
  26. protected $dbVersion = "1.0";
  27. public function __construct()
  28. {
  29. global $wpdb;
  30. $this->pdb = $wpdb;
  31. }
  32. public function install()
  33. {
  34. $this->createSchema();
  35. $this->updateOptions($this->detectOptions());
  36. }
  37. public function uninstall()
  38. {
  39. }
  40. public function checkUpdate()
  41. {
  42. if (get_site_option('deploymint_db_version') != $this->dbVersion) {
  43. $this->createSchema();
  44. }
  45. }
  46. public function setup()
  47. {
  48. add_action('init', array($this, 'initHandler'));
  49. add_action('wp_enqueue_scripts', array($this, 'enqueueScripts'));
  50. add_action('wp_ajax_deploymint_createProject', array($this, 'actionCreateProject'));
  51. add_action('wp_ajax_deploymint_deleteProject', array($this, 'actionRemoveProject'));
  52. add_action('wp_ajax_deploymint_reloadProjects', array($this, 'actionReloadProjects'));
  53. add_action('wp_ajax_deploymint_addBlogToProject', array($this, 'actionAddBlogToProject'));
  54. add_action('wp_ajax_deploymint_removeBlogFromProject', array($this, 'actionRemoveBlogFromProject'));
  55. add_action('wp_ajax_deploymint_updateOrigin', array($this, 'actionUpdateOrigin'));
  56. add_action('wp_ajax_deploymint_updateTables', array($this, 'actionUpdateTables'));
  57. add_action('wp_ajax_deploymint_updateCreateSnapshot', array($this, 'actionGetProjectBlogs'));
  58. add_action('wp_ajax_deploymint_createSnapshot', array($this, 'actionCreateSnapshot'));
  59. add_action('wp_ajax_deploymint_updateDeploySnapshot', array($this, 'actionGetDeployOptions'));
  60. add_action('wp_ajax_deploymint_deploySnapshot', array($this, 'actionDeploySnapshot'));
  61. add_action('wp_ajax_deploymint_archiveSnapshot', array($this, 'actionArchiveSnapshot'));
  62. add_action('wp_ajax_deploymint_deploy', array($this, 'ajax_deploy_callback'));
  63. add_action('wp_ajax_deploymint_updateSnapDesc', array($this, 'ajax_updateSnapDesc_callback'));
  64. add_action('wp_ajax_deploymint_undoDeploy', array($this, 'ajax_undoDeploy_callback'));
  65. add_action('wp_ajax_deploymint_deleteBackups', array($this, 'ajax_deleteBackups_callback'));
  66. add_action('wp_ajax_deploymint_updateOptions', array($this, 'ajax_updateOptions_callback'));
  67. $opt = $this->getOptions();
  68. DeployMintProjectTools::setGit($opt['git']);
  69. }
  70. protected function createSchema()
  71. {
  72. $schemaVersion = get_option("deploymint_db_version", null);
  73. if ($schemaVersion != $this->dbVersion) {
  74. $tables = array();
  75. $tables['options'] = "CREATE TABLE dep_options (
  76. name varchar(100) NOT NULL PRIMARY KEY,
  77. val varchar(255) default ''
  78. ) default charset=utf8";
  79. $tables['projects'] = "CREATE TABLE dep_projects (
  80. id int UNSIGNED NOT NULL auto_increment PRIMARY KEY,
  81. ctime int UNSIGNED NOT NULL,
  82. name varchar(100) NOT NULL,
  83. dir varchar(120) NOT NULL,
  84. origin varchar(255) NOT NULL,
  85. tables varchar(255) NOT NULL,
  86. project_uuid varchar(36) NOT NULL,
  87. deleted tinyint UNSIGNED default 0
  88. ) default charset=utf8";
  89. $tables['members'] = "CREATE TABLE dep_members (
  90. blog_id int UNSIGNED NOT NULL,
  91. project_id int UNSIGNED NOT NULL,
  92. deleted tinyint UNSIGNED default 0,
  93. KEY k1(blog_id, project_id)
  94. ) default charset=utf8";
  95. $tables['blogs'] = "CREATE TABLE dep_blogs (
  96. id int UNSIGNED NOT NULL auto_increment PRIMARY KEY,
  97. blog_url varchar(255) NOT NULL,
  98. blog_name varchar(255) NOT NULL,
  99. blog_uuid varchar(36) NOT NULL,
  100. ignore_cert tinyint UNSIGNED default 0,
  101. deleted tinyint UNSIGNED default 0
  102. ) default charset=utf8";
  103. require_once ABSPATH . 'wp-admin/includes/upgrade.php';
  104. foreach($tables as $sql) {
  105. dbDelta($sql);
  106. /*$success = $this->pdb->query($sql);
  107. if (!$success) {
  108. die($this->pdb->print_error());
  109. }*/
  110. }
  111. }
  112. if ($schemaVersion == null) {
  113. add_option("deploymint_db_version", $this->dbVersion);
  114. } else {
  115. update_option("deploymint_db_version", $this->dbVersion);
  116. }
  117. }
  118. protected function detectOptions()
  119. {
  120. $options = $this->getOptions();
  121. foreach (array('git', 'mysql', 'mysqldump', 'rsync') as $n) {
  122. $options[$n] = $options[$n] ? $options[$n] : trim(DeployMintTools::mexec("which $n"));
  123. }
  124. return $options;
  125. }
  126. protected function updateOptions($o)
  127. {
  128. foreach ($o as $n => $v) {
  129. $this->setOption($n, $v);
  130. }
  131. }
  132. protected function setOption($name, $val)
  133. {
  134. $this->pdb->query($this->pdb->prepare("INSERT INTO dep_options (name, val) VALUES (%s, %s) ON DUPLICATE KEY UPDATE val=%s", $name, $val, $val));
  135. }
  136. protected function getOptions($createTemporaryDatabase = false, $createBackupDatabase = false)
  137. {
  138. $res = $this->pdb->get_results("SELECT name, val FROM dep_options", ARRAY_A);
  139. $options = $this->getDefaultOptions();
  140. for ($i = 0; $i < sizeof($res); $i++) {
  141. $options[$res[$i]['name']] = $res[$i]['val'];
  142. }
  143. $options['backupDisabled'] = ($options['backupDisabled'] == '1');
  144. $options['temporaryDatabaseCreated'] = false;
  145. if ($options['temporaryDatabase'] == '' && $createTemporaryDatabase) {
  146. for ($i = 1; $i < 10; $i++) {
  147. $options['temporaryDatabase'] = 'dep_tmpdb' . preg_replace('/\./', '', microtime(true));
  148. $res = $this->pdb->get_results("SHOW TABLES FROM " . $options['temporaryDatabase'], ARRAY_A);
  149. if (sizeof($res) < 1) {
  150. break;
  151. }
  152. if ($i > 4) {
  153. throw new Exception("We could not create a temporary database name after 5 tries. You may not have the create DB privelege.");
  154. }
  155. }
  156. $this->pdb->query("CREATE DATABASE " . $options['temporaryDatabase']);
  157. $options['temporaryDatabaseCreated'] = true;
  158. }
  159. $options['backupDatabaseCreated'] = false;
  160. if ($createBackupDatabase && !$options['backupDisabled'] && ($options['numBackups'] != 1 || ($options['numBackups'] == 1 && $options['backupDatabase'] == ''))) {
  161. $dbPrefix = ($options['backupDatabase'] == '') ? 'depbak' : $options['backupDatabase'];
  162. $options['backupDatabase'] = $dbPrefix . '__' . preg_replace('/\./', '', microtime(true));
  163. $success = $this->pdb->query('CREATE DATABASE ' . $options['backupDatabase']);
  164. if ($succss === false){
  165. throw new Exception('Could not create backup database. ' . mysql_error($dbh));
  166. }
  167. $options['backupDatabaseCreated'] = true;
  168. }
  169. return $options;
  170. }
  171. protected function allOptionsSet()
  172. {
  173. $options = $this->getOptions();
  174. foreach (array('git', 'mysql', 'mysqldump', 'rsync', 'datadir') as $v) {
  175. if (!$options[$v]) {
  176. return false;
  177. }
  178. }
  179. if (!preg_match('/^\d+$/', $options['numBackups'])) {
  180. return false;
  181. }
  182. return true;
  183. }
  184. protected function getDefaultOptions()
  185. {
  186. return $this->defaultOptions;
  187. }
  188. protected function checkPerms()
  189. {
  190. if (!is_user_logged_in()) {
  191. die("<h2>You are not logged in.</h2>");
  192. }
  193. if ( (is_multisite() && !current_user_can('manage_network'))
  194. || !is_multisite() && !current_user_can('manage_options')) {
  195. die("<h2>You don't have permission to access this page.</h2><p>You need the 'manage_network' Super Admin capability to use DeployMint.</p>");
  196. }
  197. }
  198. protected function ajaxError($msg)
  199. {
  200. die(json_encode(array('err' => $msg)));
  201. }
  202. public function setPdb($pdb)
  203. {
  204. $this->pdb = $pdb;
  205. }
  206. public function getPdb()
  207. {
  208. return $this->pdb;
  209. }
  210. public function enqueueScripts()
  211. {
  212. //wp_deregister_script( 'jquery' );
  213. //wp_enqueue_script('jquery', plugin_dir_url( __FILE__ ) . 'js/jquery-1.6.2.js', array( ) );
  214. }
  215. public function initHandler()
  216. {
  217. if (is_admin()) {
  218. wp_enqueue_script('jquery-templates', plugin_dir_url(__FILE__) . 'js/jquery.tmpl.min.js', array('jquery'));
  219. wp_enqueue_script('jquery-leadmodal-js', plugin_dir_url(__FILE__) . 'js/jquery.easyModal.min.js', array('jquery'));
  220. wp_enqueue_script('deploymint-js', plugin_dir_url(__FILE__) . 'js/deploymint.js', array('jquery'));
  221. wp_localize_script('deploymint-js', 'DeployMintVars', array(
  222. 'ajaxURL' => admin_url('admin-ajax.php')
  223. ));
  224. wp_register_style('DeployMintCSS', plugins_url('css/admin.css', __FILE__));
  225. wp_enqueue_style('DeployMintCSS');
  226. }
  227. }
  228. public function actionIndex()
  229. {
  230. if (!$this->allOptionsSet()) {
  231. return $this->actionOptions();
  232. }
  233. include 'views/index.php';
  234. }
  235. public function actionManageProjects()
  236. {
  237. return $this->actionIndex();
  238. }
  239. public function actionManageBlogs()
  240. {
  241. if (!$this->allOptionsSet()) {
  242. return $this->actionOptions();
  243. }
  244. include 'views/manageBlogs.php';
  245. }
  246. public function actionManageProject($id)
  247. {
  248. extract($this->getOptions(), EXTR_OVERWRITE);
  249. if (!$this->allOptionsSet()) {
  250. return $this->actionOptions();
  251. }
  252. $this->checkPerms();
  253. if (!$this->allOptionsSet()) {
  254. echo '<div class="wrap"><h2 class="depmintHead">Please visit the options page and configure all options</h2></div>';
  255. return;
  256. }
  257. $proj = $this->getProject($id);
  258. $dir = $datadir . $proj['dir'] . '/';
  259. DeployMintProjectTools::setRemote($dir, $proj['origin']);
  260. if (strlen($proj['origin']) > 0 && !DeployMintProjectTools::connectedToRemote($dir)) {
  261. $this->showMessage("Please ensure that your project data directory ($dir) has access to its remote repository at {$proj['origin']}");
  262. return;
  263. }
  264. include 'views/manageProject.php';
  265. }
  266. public function actionRevert()
  267. {
  268. $this->checkPerms();
  269. if (!$this->allOptionsSet()) {
  270. return $this->actionOptions();
  271. }
  272. extract($this->getOptions(), EXTR_OVERWRITE);
  273. $dbuser = DB_USER;
  274. $dbpass = DB_PASSWORD;
  275. $dbhost = DB_HOST;
  276. $dbname = DB_NAME;
  277. $dbh = mysql_connect($dbhost, $dbuser, $dbpass, true);
  278. mysql_select_db($dbname, $dbh);
  279. $res1 = mysql_query("SHOW DATABASES", $dbh);
  280. if (mysql_error($dbh)) {
  281. $this->ajaxError("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  282. }
  283. function readBackupData($dbname, $dbh)
  284. {
  285. $res2 = mysql_query("SELECT * FROM $dbname.dep_backupdata", $dbh);
  286. if (mysql_error($dbh)) {
  287. $this->ajaxError("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  288. }
  289. $dbData = array();
  290. while ($row2 = mysql_fetch_array($res2, MYSQL_ASSOC)) {
  291. $dbData[$row2['name']] = $row2['val'];
  292. }
  293. $dbData['dbname'] = $dbname;
  294. $dbData['deployTimeH'] = date('l jS \of F Y h:i:s A', sprintf('%d', $dbData['deployTime']));
  295. return $dbData;
  296. }
  297. $dbs = array();
  298. if ($backupDatabase == '' || ($numBackups != 1)) {
  299. while ($row1 = mysql_fetch_array($res1, MYSQL_NUM)) {
  300. $dbPrefix = ($backupDatabase == '') ? 'depbak' : $backupDatabase;
  301. if (preg_match('/^' . $dbPrefix . '__/', $row1[0])) {
  302. array_push($dbs, readBackupData($row1[0], $dbh));
  303. }
  304. }
  305. function deployTimeSort($b, $a)
  306. {
  307. if ($a['deployTime'] == $b['deployTime']) {
  308. return 0;
  309. } return ($a['deployTime'] < $b['deployTime']) ? -1 : 1;
  310. }
  311. usort($dbs, 'deployTimeSort');
  312. } else {
  313. if (!$backupDisabled) {
  314. array_push($dbs, readBackupData($backupDatabase, $dbh));
  315. }
  316. }
  317. include 'views/revert.php';
  318. }
  319. public function actionOptions()
  320. {
  321. $opt = $this->getOptions();
  322. include 'views/options.php';
  323. }
  324. public function actionHelp()
  325. {
  326. include 'views/help.php';
  327. }
  328. public function __call($name, $args)
  329. {
  330. $matches = array();
  331. if (preg_match('/^actionManageProject_(\d+)$/', $name, $matches)) {
  332. $this->actionManageProject($matches[1]);
  333. } else {
  334. die("Method $name doesn't exist!");
  335. }
  336. }
  337. public function actionCreateProject()
  338. {
  339. $this->checkPerms();
  340. $name = $_POST['name'];
  341. $origin = $_POST['origin'];
  342. try {
  343. if ($this->createProject($name, $origin, $tables)) {
  344. die(json_encode(array('ok' => 1)));
  345. }
  346. } catch (Exception $e) {
  347. die(json_encode(array('err' => $e->getMessage())));
  348. }
  349. }
  350. public function actionRemoveProject()
  351. {
  352. $this->checkPerms();
  353. $id = $_POST['id'];
  354. try {
  355. if ($this->removeProject($id)) {
  356. die(json_encode(array('ok' => 1)));
  357. }
  358. } catch (Exception $e) {
  359. die(json_encode(array('err' => $e->getMessage())));
  360. }
  361. }
  362. public function actionReloadProjects()
  363. {
  364. $this->checkPerms();
  365. extract($this->getOptions(), EXTR_OVERWRITE);
  366. try {
  367. $projects = $this->getProjects();
  368. for ($i = 0; $i < sizeof($projects); $i++) {
  369. $dir = $datadir . $projects[$i]['dir'] . '/';
  370. DeployMintProjectTools::setRemote($dir, $projects[$i]['origin']);
  371. $fetch = DeployMintProjectTools::fetch($dir);
  372. $projects[$i]['originAvailable'] = !DeployMintProjectTools::isFatalResponse($fetch);
  373. $projects[$i]['originAvailableMessage'] = trim($fetch);
  374. $projects[$i]['memberBlogs'] = $this->getProjectBlogs($projects[$i]['id']);
  375. $projects[$i]['nonmemberBlogs'] = $this->getBlogsNotInProject($projects[$i]['id']);
  376. $projects[$i]['numNonmembers'] = sizeof($projects[$i]['nonmemberBlogs']);
  377. }
  378. die(json_encode(array(
  379. 'projects' => $projects
  380. )));
  381. } catch (Exception $e){
  382. die(json_encode(array('err' => $e->getMessage())));
  383. }
  384. }
  385. public function actionAddBlogToProject()
  386. {
  387. $this->checkPerms();
  388. try {
  389. if ($this->addBlogToProject($_POST['blogID'], $_POST['projectID'], $_POST['username'], $_POST['password'])) {
  390. die(json_encode(array('ok' => 1)));
  391. }
  392. } catch (Exception $e){
  393. die(json_encode(array('err' => $e->getMessage())));
  394. }
  395. }
  396. public function actionRemoveBlogFromProject()
  397. {
  398. $this->checkPerms();
  399. try {
  400. if ($this->removeBlogFromProject($_POST['blogID'], $_POST['projectID'], $_POST['username'], $_POST['password'])) {
  401. die(json_encode(array('ok' => 1)));
  402. }
  403. } catch (Exception $e){
  404. die(json_encode(array('err' => $e->getMessage())));
  405. }
  406. }
  407. public function actionUpdateOrigin()
  408. {
  409. $this->checkPerms();
  410. try {
  411. if ($this->updateOrigin($_POST['projectId'], $_POST['origin'])) {
  412. die(json_encode(array('ok' => 1)));
  413. }
  414. } catch (Exception $e){
  415. die(json_encode(array('err' => $e->getMessage())));
  416. }
  417. }
  418. protected function updateOrigin($projectId, $origin)
  419. {
  420. $opt = $this->getOptions();
  421. $proj = $this->getProject($projectId);
  422. $dir = $opt['datadir'] . $proj['dir'] . '/';
  423. $this->pdb->update('dep_projects', array('origin'=>$origin), array('id'=>$projectId));
  424. DeployMintProjectTools::setRemote($dir, $origin);
  425. return true;
  426. }
  427. public function actionUpdateTables()
  428. {
  429. $this->checkPerms();
  430. try {
  431. if ($this->updateTables($_POST['projectId'], $_POST['tables'])) {
  432. die(json_encode(array('ok' => 1)));
  433. }
  434. } catch (Exception $e){
  435. die(json_encode(array('err' => $e->getMessage())));
  436. }
  437. }
  438. protected function updateTables($projectId, $tables)
  439. {
  440. $proj = $this->getProject($projectId);
  441. $this->pdb->update('dep_projects', array('tables'=>$tables), array('id'=>$projectId));
  442. return true;
  443. }
  444. public function actionGetProjectBlogs()
  445. {
  446. $this->checkPerms();
  447. try {
  448. $blogs = $this->getProjectBlogs($_REQUEST['projectid']);
  449. die(json_encode(array('blogs'=>$blogs)));
  450. } catch (Exception $e){
  451. die(json_encode(array('err' => $e->getMessage())));
  452. }
  453. }
  454. protected function projectExists($name)
  455. {
  456. $result = $this->pdb->get_results($this->pdb->prepare("SELECT name FROM dep_projects WHERE name=%s AND deleted=0", $name), ARRAY_A);
  457. return sizeof($result) > 0;
  458. }
  459. protected function createProject($name, $origin, $tables="", $uuid=null)
  460. {
  461. $this->checkPerms();
  462. $opt = $this->getOptions();
  463. extract($opt, EXTR_OVERWRITE);
  464. if ($this->projectExists($name)) {
  465. throw new Exception('A project with that name already exists');
  466. }
  467. if ($uuid == null) {
  468. $uuid = DeployMintTools::generateUUID();
  469. }
  470. $dir = $name;
  471. $dir = preg_replace('/[^a-zA-Z0-9]+/', '_', $dir);
  472. $fulldir = $dir . '-1';
  473. $counter = 2;
  474. while (is_dir($datadir . $fulldir)) {
  475. $fulldir = preg_replace('/\-\d+$/', '', $fulldir);
  476. $fulldir .= '-' . $counter;
  477. $counter++;
  478. if ($counter > 1000) {
  479. throw new Exception('Too many directories already exist starting with "'.$dir.'"');
  480. }
  481. }
  482. $finaldir = $datadir . $fulldir;
  483. if (!@mkdir($finaldir, 0755)) {
  484. throw new Exception('Could not create directory ' . $finaldir);
  485. }
  486. $git1 = DeployMintTools::mexec("$git init ; $git add . ", $finaldir);
  487. if (strlen($origin) > 0) {
  488. DeployMintProjectTools::setRemote($finaldir, $origin);
  489. DeployMintProjectTools::fetch($finaldir);
  490. }
  491. $insertToDb = $this->pdb->query($this->pdb->prepare("INSERT INTO dep_projects (ctime, name, dir, origin, tables, project_uuid) VALUES (unix_timestamp(), %s, %s, %s, %s, %s)", $name, $fulldir, $origin, $tables, $uuid));
  492. if ($insertToDb) {
  493. return true;
  494. } else {
  495. rmdir($finaldir);
  496. throw new Exception($this->pdb->last_error);
  497. }
  498. }
  499. protected function removeProject($id)
  500. {
  501. $this->pdb->query($this->pdb->prepare("UPDATE dep_members SET deleted=1 WHERE project_id=%d", $_POST['blogID'], $_POST['projectID']));
  502. $this->pdb->query($this->pdb->prepare("UPDATE dep_projects SET deleted=1 WHERE id=%d", $_POST['projectID']));
  503. return true;
  504. }
  505. protected function getProject($id)
  506. {
  507. return $this->pdb->get_row($this->pdb->prepare("SELECT * FROM dep_projects WHERE id=%d AND deleted=0", array($id)), ARRAY_A);
  508. }
  509. protected function getProjectByName($name)
  510. {
  511. return $this->pdb->get_row($this->pdb->prepare("SELECT * FROM dep_projects WHERE name=%s AND deleted=0", array($name)), ARRAY_A);
  512. }
  513. protected function getProjectByUUID($uuid)
  514. {
  515. return $this->pdb->get_row($this->pdb->prepare("SELECT * FROM dep_projects WHERE project_uuid=%s AND deleted=0", array($uuid)), ARRAY_A);
  516. }
  517. protected function getProjects()
  518. {
  519. return $this->pdb->get_results("SELECT * FROM dep_projects WHERE deleted=0", ARRAY_A);
  520. }
  521. protected function getProjectBlogs($project)
  522. {
  523. $blogsTable = $this->pdb->base_prefix . 'blogs';
  524. return $this->pdb->get_results($this->pdb->prepare("SELECT $blogsTable.blog_id AS blog_id, $blogsTable.domain AS domain, $blogsTable.path AS path, $blogsTable.blog_name AS name FROM dep_members, $blogsTable WHERE dep_members.deleted=0 AND dep_members.project_id=%d AND dep_members.blog_id = $blogsTable.blog_id", $project), ARRAY_A);
  525. }
  526. protected function getBlogsIds()
  527. {
  528. $blogs = $this->getBlogs();
  529. $ids = array();
  530. foreach($blogs as $b) {
  531. $ids[] = $b['blog_id'];
  532. }
  533. return $ids;
  534. }
  535. protected function getProjectBlogsIds($projectId)
  536. {
  537. $blogs = $this->getProjectBlogs($projectId);
  538. $ids = array();
  539. foreach($blogs as $b) {
  540. $ids[] = $b['blog_id'];
  541. }
  542. return $ids;
  543. }
  544. protected function getBlogsNotInProject($project)
  545. {
  546. $availableBlogs = array();
  547. $allBlogs = $this->getBlogs();
  548. $projectBlogs = $this->getProjectBlogs($project);
  549. if (sizeof($projectBlogs) == 0) {
  550. return $allBlogs;
  551. }
  552. $pBlogIds = array_map(function($e){
  553. return $e['blog_id'];
  554. }, $projectBlogs);
  555. foreach($allBlogs as $b) {
  556. if (!in_array($b['blog_id'], $pBlogIds)) {
  557. $availableBlogs[] = $b;
  558. }
  559. }
  560. return $availableBlogs;
  561. }
  562. protected function getBlog($id)
  563. {
  564. $blogsTable = $this->pdb->base_prefix . 'blogs';
  565. return $this->pdb->get_row($this->pdb->prepare("SELECT * FROM $blogsTable WHERE blog_id = %d ORDER BY domain ASC", array($id)), ARRAY_A);
  566. }
  567. protected function getBlogs()
  568. {
  569. $blogsTable = $this->pdb->base_prefix . 'blogs';
  570. return $this->pdb->get_results("SELECT blog_id, domain, path FROM $blogsTable ORDER BY domain ASC", ARRAY_A);
  571. }
  572. protected function removeBlogFromProject($blogId, $projectId, $username=null, $password=null)
  573. {
  574. $this->pdb->query($this->pdb->prepare("UPDATE dep_members SET deleted=1 WHERE blog_id=%d and project_id=%d", $blogId, $projectId));
  575. return true;
  576. }
  577. protected function addBlogToProject($blogId, $projectId, $username=null, $password=null)
  578. {
  579. if (!$this->projectHasBlog($blogId, $projectId)) {
  580. $this->pdb->query($this->pdb->prepare("INSERT INTO dep_members (blog_id, project_id) VALUES (%d, %d)", $blogId, $projectId));
  581. }
  582. return true;
  583. }
  584. protected function projectHasBlog($blogId, $projectId)
  585. {
  586. $result = $this->pdb->get_results($this->pdb->prepare("SELECT project_id FROM dep_members WHERE blog_id=%d AND project_id=%d AND deleted=0", array($blogId, $projectId)), ARRAY_A);
  587. return sizeof($result) > 0;
  588. }
  589. public function actionCreateSnapshot()
  590. {
  591. $this->checkPerms();
  592. try {
  593. if ($this->createSnapshot($_REQUEST['projectid'], $_REQUEST['blogid'], $_REQUEST['name'], $_REQUEST['desc'], $_REQUEST['username'], $_REQUEST['password'])) {
  594. die(json_encode(array('ok' => 1)));
  595. }
  596. } catch (Exception $e){
  597. //$this->ajaxError($e->getMessage());
  598. die(json_encode(array('err' => $e->getMessage())));
  599. }
  600. }
  601. protected function createSnapshot($projectId, $blogId, $name, $desc, $username=null, $password=null)
  602. {
  603. // Validate name and description
  604. if (!preg_match('/\w+/', $name)) {
  605. throw new Exception("Please enter a name for this snapshot");
  606. }
  607. if (strlen($name) > 20) {
  608. throw new Exception("Your snapshot name must be 20 characters or less.");
  609. }
  610. if (preg_match('/[^a-zA-Z0-9\_\-\.]/', $name)) {
  611. throw new Exception("Your snapshot name can only contain characters a-z A-Z 0-9 and dashes, underscores and dots.");
  612. }
  613. if (!$desc) {
  614. throw new Exception("Please enter a description for this snapshot.");
  615. }
  616. $prec = $this->getProject($projectId);
  617. if (sizeof($prec) < 1) {
  618. throw new Exception("That project doesn't exist.");
  619. }
  620. return true;
  621. }
  622. protected function getTablePrefix($blogId)
  623. {
  624. return $this->pdb->base_prefix;
  625. }
  626. protected function copyFilesToDataDir($blogId, $dest){}
  627. protected function copyFilesFromDataDir($blogId, $src, $deployParts=array()){}
  628. /**
  629. * Gets the various aspects that could potentially be included in the deployment
  630. */
  631. protected function getAvailableDeployParts()
  632. {
  633. return array(
  634. self::DP_TABLE_ALL => 'Database Tables',
  635. self::DP_DIR_UPLOADS => 'Uploaded Media',
  636. );
  637. }
  638. protected function getTablesToSnapshot($projectId=0, $prefix='')
  639. {
  640. $stdTables = $this->getTablesWithPrefix($prefix);
  641. $projTables = $this->getProjectSpecificTables($projectId, $prefix);
  642. return array_merge($stdTables, $projTables);
  643. }
  644. protected function getTablesToDeploy($projectId=0, $prefix='')
  645. {
  646. $stdTables = array();
  647. foreach($this->wpTables as $key=>$value) {
  648. $stdTables[$key] = $prefix . $value;
  649. }
  650. $projTables = $this->getProjectSpecificTables($projectId, $prefix);
  651. return array_merge($stdTables, $projTables);
  652. }
  653. protected function getTablesWithPrefix($prefix, $pdb=null)
  654. {
  655. if ($pdb == null) {
  656. $pdb = $this->getPdb();
  657. }
  658. $tables = $pdb->get_results("SHOW TABLES", ARRAY_N);
  659. $prefixedTables = array();
  660. foreach($tables as $table) {
  661. $t = trim($table[0]);
  662. if (strlen($t) && preg_match("/^$prefix/", $t)) {
  663. $prefixedTables[] = $t;
  664. }
  665. }
  666. return $prefixedTables;
  667. }
  668. protected function getProjectSpecificTables($projectId=0, $prefix='')
  669. {
  670. $projTables = array();
  671. if ($projectId) {
  672. $project = $this->getProject($projectId);
  673. if (strlen(trim($project['tables']))) {
  674. $projTables = explode(',', $project['tables']);
  675. foreach($projTables as $key=>$value) {
  676. $v = trim($value);
  677. if (strlen($v) > 0) {
  678. if (substr($v, 0, 1) == '\\') {
  679. $projTables[$key] = $v;
  680. } else {
  681. $projTables[$key] = $prefix . $v;
  682. }
  683. }
  684. }
  685. }
  686. }
  687. return $projTables;
  688. }
  689. protected function doSnapshot($projectId, $blogId, $name, $desc)
  690. {
  691. $this->checkPerms();
  692. $opt = $this->getOptions();
  693. extract($opt, EXTR_OVERWRITE);
  694. $proj = $this->getProject($projectId);
  695. $dir = $datadir . $proj['dir'] . '/';
  696. $pdb = $this->getPdb();
  697. // Check that project directory exists
  698. if (!is_dir($dir)) {
  699. throw new Exception("The directory " . $dir . " for this project doesn't exist for some reason. Did you delete it?");
  700. }
  701. // Make sure we are current from the remote
  702. DeployMintProjectTools::fetch($dir);
  703. // Make sure project directory is a working git directory
  704. if (!DeployMintProjectTools::isGitRepo($dir)) {
  705. throw new Exception("The directory $dir is not a valid git repository. The output we received is: $branchOut");
  706. }
  707. // Check if branch exists
  708. if (DeployMintProjectTools::branchExists($dir, $name)) {
  709. throw new Exception("A snapshot with the name $name already exists. Please choose another.");
  710. }
  711. $cout1 = DeployMintTools::mexec("$git checkout master 2>&1", $dir);
  712. //Before we do our initial commit we will get an error trying to checkout master because it doesn't exist.
  713. if (!preg_match("/(?:Switched to branch|Already on|error: pathspec 'master' did not match)/", $cout1)) {
  714. throw new Exception("We could not switch the git repository in $dir to 'master'. The output was: $cout1");
  715. }
  716. $prefix = $this->getTablePrefix($blogId);
  717. $prefixFile = $dir . 'deployData.txt';
  718. $fh2 = fopen($prefixFile, 'w');
  719. if (!fwrite($fh2, $prefix . ':' . microtime(true))) {
  720. throw new Exception("We could not write to deployData.txt in the directory $dir");
  721. }
  722. fclose($fh2);
  723. // Add the Media locations
  724. $this->copyFilesToDataDir($blogId, $dir);
  725. $add = DeployMintProjectTools::git('add .', $dir);
  726. $siteURLRes = $this->pdb->get_row("SELECT option_name, option_value FROM {$prefix}options WHERE option_name = 'siteurl'", ARRAY_A);
  727. $siteURL = $siteURLRes['option_value'];
  728. $desc = "Snapshot of: $siteURL\n" . $desc;
  729. $dumpErrs = array();
  730. $tables = $this->getTablesToSnapshot($projectId, $prefix);
  731. DeployMintTools::mexec("rm -fr $dir/*.sql");
  732. foreach ($tables as $t) {
  733. $tableFile = preg_replace("/^$prefix/", '', $t) . '.sql';
  734. $tableName = $t;
  735. $path = $dir . $tableFile;
  736. $dbuser = DB_USER;
  737. $dbpass = DB_PASSWORD;
  738. $dbhost = DB_HOST;
  739. $dbname = DB_NAME;
  740. $o1 = DeployMintTools::mexec("$mysqldump --skip-comments --extended-insert --complete-insert --skip-comments -u $dbuser -p$dbpass -h $dbhost $dbname $tableName > $path 2>&1", $dir);
  741. if (preg_match('/\w+/', $o1)) {
  742. array_push($dumpErrs, $o1);
  743. } else {
  744. $grepOut = DeployMintTools::mexec("grep CREATE $path 2>&1");
  745. if (!preg_match('/CREATE/', $grepOut)) {
  746. array_push($dumpErrs, "We could not create a valid table dump file for $tableName");
  747. } else {
  748. $gitAddOut = DeployMintTools::mexec("$git add $tableFile 2>&1", $dir);
  749. if (preg_match('/\w+/', $gitAddOut)) {
  750. throw new Exception("We encountered an error running '$git add $tableFile' the error was: $gitAddOut");
  751. }
  752. }
  753. }
  754. }
  755. if (sizeof($dumpErrs) > 0) {
  756. $resetOut = DeployMintProjectTools::git('reset --hard HEAD', $dir);
  757. if (!preg_match('/HEAD is now at/', $resetOut)) {
  758. throw new Exception("Errors occured during mysqldump and we could not revert the git repository in $dir back to it's original state using '$git reset --hard HEAD'. The output we got was: " . $resetOut);
  759. }
  760. throw new Exception("Errors occured during mysqldump: " . implode(', ', $dumpErrs));
  761. }
  762. $tmpfile = $datadir . microtime(true) . '.tmp';
  763. $fh = fopen($tmpfile, 'w');
  764. fwrite($fh, $desc);
  765. fclose($fh);
  766. global $current_user;
  767. get_currentuserinfo();
  768. $user_name = trim($current_user->user_firstname . ' ' . $current_user->user_lastname);
  769. if ($user_name == '') {
  770. $user_name = $current_user->display_name;
  771. }
  772. $commitUser = $user_name . ' <' . $current_user->user_email . '>';
  773. $commitOut2 = DeployMintProjectTools::git("commit --author=\"$commitUser\" -a -F \"$tmpfile\"", $dir);
  774. unlink($tmpfile);
  775. if (!preg_match('/files changed/', $commitOut2)) {
  776. throw new Exception("git commit failed. The output we got was: $commitOut2");
  777. }
  778. $brOut2 = DeployMintProjectTools::git('branch ' . $name, $dir);
  779. if (preg_match('/\w+/', $brOut2)) {
  780. throw new Exception("We encountered an error running '$git branch $name' the output was: $brOut2");
  781. }
  782. DeployMintProjectTools::git('push origin ' . $name, $dir);
  783. return true;
  784. }
  785. public function actionGetDeployOptions()
  786. {
  787. $this->checkPerms();
  788. try {
  789. $blogs = $this->getProjectBlogs($_REQUEST['projectid']);
  790. $snapshots = $this->getProjectSnapshots($_REQUEST['projectid']);
  791. die(json_encode(array('blogs'=>$blogs,'snapshots'=>$snapshots,'deployParts'=>$this->getAvailableDeployParts())));
  792. } catch (Exception $e){
  793. die(json_encode(array('err' => $e->getMessage())));
  794. }
  795. }
  796. protected function getProjectSnapshots($projectId)
  797. {
  798. $opt = $this->getOptions();
  799. extract($opt, EXTR_OVERWRITE);
  800. $project = $this->getProject($projectId);
  801. $dir = $datadir . $project['dir'];
  802. if (!is_dir($dir)) {
  803. throw new Exception("The directory $dir for this project does not exist.");
  804. }
  805. $fetch = DeployMintProjectTools::fetch($dir);
  806. $branches = DeployMintProjectTools::getAllBranches($dir);
  807. $bprefix = DeployMintProjectTools::remoteExists($dir) ? 'remotes/origin/' : '';
  808. $snapshots = array();
  809. for ($i = 0; $i < sizeof($branches); $i++) {
  810. if (preg_match('/\w+/', $branches[$i])) {
  811. $bname = $branches[$i];
  812. if ($bname == 'master') {
  813. continue;
  814. }
  815. $dateOut = DeployMintTools::mexec("$git log -n 1 {$bprefix}{$bname} | grep Date 2>&1", $dir);
  816. $m = '';
  817. if (preg_match('/Date:\s+(.+)$/', $dateOut, $m)) {
  818. $ctime = strtotime($m[1]);
  819. $date = $m[1];
  820. array_push($snapshots, array('name' => $branches[$i], 'created' => $date, 'ctime' => $ctime));
  821. }
  822. } else {
  823. unset($branches[$i]);
  824. }
  825. }
  826. if (sizeof($snapshots) > 0) {
  827. function ctimeSort($b, $a)
  828. {
  829. if ($a['ctime'] == $b['ctime']) {
  830. return 0;
  831. } return ($a['ctime'] < $b['ctime']) ? -1 : 1;
  832. }
  833. usort($snapshots, 'ctimeSort');
  834. }
  835. return $snapshots;
  836. }
  837. public function actionDeploySnapshot()
  838. {
  839. $this->checkPerms();
  840. try {
  841. if ($this->deploySnapshot($_REQUEST['name'], $_REQUEST['blogid'], $_REQUEST['projectid'], $_REQUEST['username'], $_REQUEST['password'], $_REQUEST['deployParts'])) {
  842. die(json_encode(array('ok' => 1)));
  843. }
  844. } catch (Exception $e){
  845. //$this->ajaxError($e->getMessage());
  846. die(json_encode(array('err' => $e->getMessage())));
  847. }
  848. }
  849. public function actionArchiveSnapshot()
  850. {
  851. $this->checkPerms();
  852. try {
  853. if ($this->archiveSnapshot($_REQUEST['projectId'], $_REQUEST['snapshots'])) {
  854. die(json_encode(array('ok' => 1)));
  855. }
  856. } catch (Exception $e){
  857. //$this->ajaxError($e->getMessage());
  858. die(json_encode(array('err' => $e->getMessage())));
  859. }
  860. }
  861. protected function deploySnapshot($snapshot, $blogId, $projectId, $username=null, $password=null, $deployParts=array())
  862. {
  863. return true;
  864. }
  865. protected function archiveSnapshot($projectId, $snapshots)
  866. {
  867. $opt = $this->getOptions(true, true);
  868. extract($opt, EXTR_OVERWRITE);
  869. $project = $this->getProject($projectId);
  870. $dir = $datadir . $project['dir'] . '/';
  871. $time = time();
  872. foreach($snapshots as $snapshot) {
  873. DeployMintProjectTools::checkout($dir, $snapshot);
  874. DeployMintProjectTools::createTag($dir, "archive/{$time}-{$snapshot}");
  875. DeployMintProjectTools::checkout($dir, $snapshot);
  876. DeployMintProjectTools::detach($dir);
  877. DeployMintProjectTools::deleteBranch($dir, $snapshot);
  878. }
  879. DeployMintProjectTools::pushTags($dir);
  880. return true;
  881. }
  882. protected function doDeploySnapshot($name, $blogid, $pid, $deployParts=array())
  883. {
  884. $this->checkPerms();
  885. $opt = $this->getOptions(true, true);
  886. extract($opt, EXTR_OVERWRITE);
  887. $leaveComments = true; //$_POST['leaveComments'];
  888. if (!preg_match('/\w+/', $name)) {
  889. throw new Exception("Please select a snapshot to deploy.");
  890. }
  891. $prec = $this->pdb->get_results($this->pdb->prepare("SELECT * FROM dep_projects WHERE id=%d AND deleted=0", $pid), ARRAY_A);
  892. if (sizeof($prec) < 1) {
  893. throw new Exception("That project doesn't exist.");
  894. }
  895. $proj = $prec[0];
  896. $dir = $datadir . $proj['dir'] . '/';
  897. $mexists = $this->pdb->get_results($this->pdb->prepare("SELECT blog_id FROM dep_members WHERE blog_id=%d AND project_id=%d AND deleted=0", $blogid, $pid), ARRAY_A);
  898. if (sizeof($mexists) < 1) {
  899. throw new Exception("That blog doesn't exist or is not a member of this project. Please select a valid blog to deploy to.");
  900. }
  901. if (!is_dir($dir)) {
  902. throw new Exception("The directory " . $dir . " for this project doesn't exist for some reason. Did you delete it?");
  903. }
  904. $co0 = DeployMintTools::mexec("$git fetch origin ; $git checkout -t origin/$name 2>&1", $dir);
  905. $co1 = DeployMintTools::mexec("$git checkout $name 2>&1", $dir);
  906. if (!preg_match('/(?:Switched|Already)/', $co1)) {
  907. throw new Exception("We could not find snapshot $name in the git repository. The error was: $co1");
  908. }
  909. $destTablePrefix = $this->getTablePrefix($blogid);
  910. $optionsToRestore = array('siteurl', 'home', 'upload_path');
  911. if ($opt['preserveBlogName']) {
  912. $optionsToRestore[] = 'blogname';
  913. }
  914. $res3 = $this->pdb->get_results("SELECT option_name, option_value FROM {$destTablePrefix}options WHERE option_name IN ('" . implode("','", $optionsToRestore) . "')", ARRAY_A);
  915. if (sizeof($res3) < 1) {
  916. throw new Exception("We could not find the data we need for the blog you're trying to deploy to.");
  917. }
  918. $options = array();
  919. for ($i = 0; $i < sizeof($res3); $i++) {
  920. $options[$res3[$i]['option_name']] = $res3[$i]['option_value'];
  921. }
  922. // Update the Media folder
  923. $this->copyFilesFromDataDir($blogid, $dir, $deployParts);
  924. // Deploy database tables
  925. if (isset($deployParts[self::DP_TABLE_ALL])) {
  926. $fh = fopen($dir . 'deployData.txt', 'r');
  927. $deployData = fread($fh, 100);
  928. $depDat = explode(':', $deployData);
  929. $sourceTablePrefix = $depDat[0];
  930. if (!$sourceTablePrefix) {
  931. throw new Exception("We could not read the table prefix from $dir/deployData.txt");
  932. }
  933. $dbuser = DB_USER;
  934. $dbpass = DB_PASSWORD;
  935. $dbhost = DB_HOST;
  936. $dbname = DB_NAME;
  937. // Import into temporary database
  938. $slurp1 = DeployMintTools::mexec("cat *.sql | $mysql -u $dbuser -p$dbpass -h $dbhost $temporaryDatabase ", $dir);
  939. if (preg_match('/\w+/', $slurp1)) {
  940. throw new Exception("We encountered an error importing the data files from snapshot $name into database $temporaryDatabase $dbuser:$dbpass@$dbhost. The error was: " . substr($slurp1, 0, 1000));
  941. }
  942. $dbh = mysql_connect($dbhost, $dbuser, $dbpass, true);
  943. if (mysql_error($dbh)) {
  944. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  945. }
  946. if (!mysql_select_db($temporaryDatabase, $dbh)) {
  947. throw new Exception("Could not select temporary database $temporaryDatabase : " . mysql_error($dbh));
  948. }
  949. $curdbres = mysql_query("SELECT DATABASE()", $dbh);
  950. $curdbrow = mysql_fetch_array($curdbres, MYSQL_NUM);
  951. if (mysql_error($dbh)) {
  952. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  953. }
  954. $destSiteURL = $options['siteurl'];
  955. $res4 = mysql_query("SELECT option_value FROM {$sourceTablePrefix}options WHERE option_name='siteurl'", $dbh);
  956. if (mysql_error($dbh)) {
  957. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  958. }
  959. if (!$res4) {
  960. throw new Exception("We could not get the siteurl from the database we're about to deploy. That could mean that we could not create the DB or the import failed.");
  961. }
  962. $row = mysql_fetch_array($res4, MYSQL_ASSOC);
  963. if (mysql_error($dbh)) {
  964. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  965. }
  966. if (!$row) {
  967. throw new Exception("We could not get the siteurl from the database we're about to deploy. That could mean that we could not create the DB or the import failed. (2)");
  968. }
  969. $sourceSiteURL = $row['option_value'];
  970. if (!$sourceSiteURL) {
  971. throw new Exception("We could not get the siteurl from the database we're about to deploy. That could mean that we could not create the DB or the import failed. (3)");
  972. }
  973. $destHost = preg_replace('/^https?:\/\/([^\/]+.*)$/i', '$1', $destSiteURL);
  974. $sourceHost = preg_replace('/^https?:\/\/([^\/]+.*)$/i', '$1', $sourceSiteURL);
  975. foreach ($options as $oname => $val) {
  976. mysql_query("UPDATE {$sourceTablePrefix}options SET option_value='" . mysql_real_escape_string($val) . "' WHERE option_name='" . mysql_real_escape_string($oname) . "'", $dbh);
  977. if (mysql_error($dbh)) {
  978. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  979. }
  980. }
  981. $res5 = mysql_query("SELECT ID, post_content, guid FROM {$sourceTablePrefix}posts", $dbh);
  982. if (mysql_error($dbh)) {
  983. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  984. }
  985. // Update wp_posts.post_content replacing sourceurl with dest url
  986. mysql_query("UPDATE {$sourceTablePrefix}posts SET post_content=REPLACE(post_content, '$sourceHost', '$destHost'), guid=REPLACE(guid, '$sourceHost', '$destHost')", $dbh);
  987. if (mysql_error($dbh)) {
  988. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  989. }
  990. // Update wp_postmeta.meta_value replacing sourceurl with dest url
  991. // For now, don't do anything with serialized data
  992. // TODO: unserialize data, update value, serialize data??? Is it worth it?
  993. mysql_query("UPDATE {$sourceTablePrefix}postmeta SET meta_value=REPLACE(meta_value, '$sourceSiteURL', '$destSiteURL')
  994. WHERE meta_value NOT LIKE 'a:%'
  995. AND meta_value NOT LIKE 's:%'
  996. AND meta_value NOT LIKE 'i:%'
  997. AND meta_value NOT LIKE 'b:%'
  998. AND meta_value NOT LIKE 'N:%'
  999. AND meta_value NOT LIKE 'O:%'
  1000. AND meta_value NOT LIKE 'd:%'", $dbh);
  1001. if (mysql_error($dbh)) {
  1002. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1003. }
  1004. // Update wp_usermeta.meta_key replacing source prefix with destination prefix
  1005. mysql_query("UPDATE {$sourceTablePrefix}usermeta SET meta_key=REPLACE(meta_key, '$sourceTablePrefix', '$destTablePrefix')
  1006. WHERE meta_key LIKE '{$sourceTablePrefix}%'", $dbh);
  1007. if (mysql_error($dbh)) {
  1008. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1009. }
  1010. mysql_query("UPDATE {$temporaryDatabase}.{$sourceTablePrefix}options SET option_name='{$destTablePrefix}user_roles' WHERE option_name='{$sourceTablePrefix}user_roles'", $dbh);
  1011. if (mysql_error($dbh)) {
  1012. throw new Exception("A database error occured while updating the user_roles option in the destination database: " . substr(mysql_error($dbh), 0, 200));
  1013. }
  1014. if ($leaveComments) {
  1015. //Delete comments from DB we're deploying
  1016. mysql_query("DELETE FROM {$sourceTablePrefix}comments", $dbh);
  1017. if (mysql_error($dbh)) {
  1018. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1019. }
  1020. mysql_query("DELETE FROM {$sourceTablePrefix}commentmeta", $dbh);
  1021. if (mysql_error($dbh)) {
  1022. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1023. }
  1024. //Bring comments across from live (destination) DB
  1025. mysql_query("INSERT INTO $temporaryDatabase.{$sourceTablePrefix}comments SELECT * FROM $dbname.{$destTablePrefix}comments", $dbh);
  1026. if (mysql_error($dbh)) {
  1027. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1028. }
  1029. mysql_query("INSERT INTO $temporaryDatabase.{$sourceTablePrefix}commentmeta SELECT * FROM $dbname.{$destTablePrefix}commentmeta", $dbh);
  1030. if (mysql_error($dbh)) {
  1031. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1032. }
  1033. //Then remap comments to posts based on the "slug" which is the post_name
  1034. $res6 = mysql_query("SELECT dp.post_name AS destPostName, dp.ID AS destID, sp.post_name AS sourcePostName, sp.ID AS sourceID FROM $dbname.{$destTablePrefix}posts AS dp, $temporaryDatabase.{$sourceTablePrefix}posts AS sp WHERE dp.post_name = sp.post_name", $dbh);
  1035. if (mysql_error($dbh)) {
  1036. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1037. }
  1038. if (!$res6) {
  1039. throw new Exception("DB error creating maps betweeb post slugs: " . mysql_error($dbh));
  1040. }
  1041. $pNameMap = array();
  1042. while ($row = mysql_fetch_array($res6, MYSQL_ASSOC)) {
  1043. $pNameMap[$row['destID']] = $row['sourceID'];
  1044. }
  1045. $res10 = mysql_query("SELECT comment_ID, comment_post_ID FROM {$sourceTablePrefix}comments", $dbh);
  1046. if (mysql_error($dbh)) {
  1047. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1048. }
  1049. while ($row = mysql_fetch_array($res10, MYSQL_ASSOC)) {
  1050. //If a post exists in the source with the same slug as the destination, then associate the destination's comments with that post.
  1051. if (array_key_exists($row['comment_post_ID'], $pNameMap)) {
  1052. mysql_query("UPDATE $sourceTablePrefix" . "comments set comment_post_ID=" . $pNameMap[$row['comment_post_ID']] . " WHERE comment_ID=" . $row['comment_ID'], $dbh);
  1053. if (mysql_error($dbh)) {
  1054. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1055. }
  1056. } else { //Otherwise delete the comment because it is associated with a post on the destination which does not exist in the source we're about to deploy
  1057. mysql_query("DELETE FROM {$sourceTablePrefix}comments WHERE comment_ID=" . $row['comment_ID'], $dbh);
  1058. if (mysql_error($dbh)) {
  1059. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1060. }
  1061. }
  1062. }
  1063. $res11 = mysql_query("SELECT ID FROM {$sourceTablePrefix}posts", $dbh);
  1064. if (mysql_error($dbh)) {
  1065. throw new Exception("A database error occured: " . substr(mysql_error($dbh), 0, 200));
  1066. }
  1067. while ($row = mysql_fetch_array($res11, MYSQL_ASSOC)) {
  1068. $res12 = mysql_query("SELECT count(*) AS cnt FROM {$sourceTablePrefix}comments WHERE comment_post_ID=" . $row['ID'], $dbh);
  1069. if (mysql_error(

Large files files are truncated, but you can click here to view the full file