wordpress_import.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. <?php
  2. /**
  3. * WordPress Database Import Tool
  4. * Imports posts, categories, users, and comments from WordPress to the publication system
  5. */
  6. class WordPressImport {
  7. private $wpDb;
  8. private $targetDb;
  9. private $wpConfig;
  10. private $importLog = [];
  11. private $errors = [];
  12. public function __construct($wpConfig) {
  13. $this->wpConfig = $wpConfig;
  14. $this->targetDb = Database::getInstance();
  15. $this->connectWordPress();
  16. }
  17. /**
  18. * Connect to WordPress database
  19. */
  20. private function connectWordPress() {
  21. try {
  22. $dsn = "mysql:host={$this->wpConfig['host']};dbname={$this->wpConfig['database']};charset=utf8mb4";
  23. $this->wpDb = new PDO($dsn, $this->wpConfig['username'], $this->wpConfig['password']);
  24. $this->wpDb->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  25. $this->wpDb->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
  26. $this->log('Connected to WordPress database successfully');
  27. } catch (Exception $e) {
  28. throw new Exception("Failed to connect to WordPress database: " . $e->getMessage());
  29. }
  30. }
  31. /**
  32. * Test WordPress connection and verify structure
  33. */
  34. public function testConnection() {
  35. try {
  36. // Check if WordPress tables exist
  37. $tables = ['wp_posts', 'wp_users', 'wp_terms', 'wp_term_taxonomy', 'wp_term_relationships', 'wp_comments'];
  38. $existingTables = [];
  39. foreach ($tables as $table) {
  40. $stmt = $this->wpDb->query("SHOW TABLES LIKE '{$table}'");
  41. if ($stmt->rowCount() > 0) {
  42. $existingTables[] = $table;
  43. }
  44. }
  45. if (count($existingTables) < 6) {
  46. throw new Exception("WordPress database structure incomplete. Missing tables: " . implode(', ', array_diff($tables, $existingTables)));
  47. }
  48. // Get basic stats
  49. $stats = [
  50. 'posts' => $this->getPostCount(),
  51. 'pages' => $this->getPageCount(),
  52. 'categories' => $this->getCategoryCount(),
  53. 'users' => $this->getUserCount(),
  54. 'comments' => $this->getCommentCount()
  55. ];
  56. return ['success' => true, 'stats' => $stats, 'tables' => $existingTables];
  57. } catch (Exception $e) {
  58. return ['success' => false, 'error' => $e->getMessage()];
  59. }
  60. }
  61. /**
  62. * Import all WordPress data
  63. */
  64. public function importAll($options = []) {
  65. $results = [];
  66. try {
  67. // Start transaction
  68. $this->targetDb->beginTransaction();
  69. // Import categories first (posts depend on them)
  70. if ($options['import_categories'] ?? true) {
  71. $results['categories'] = $this->importCategories();
  72. }
  73. // Import users
  74. if ($options['import_users'] ?? true) {
  75. $results['users'] = $this->importUsers();
  76. }
  77. // Import posts
  78. if ($options['import_posts'] ?? true) {
  79. $results['posts'] = $this->importPosts();
  80. }
  81. // Import comments
  82. if ($options['import_comments'] ?? true) {
  83. $results['comments'] = $this->importComments();
  84. }
  85. // Commit transaction
  86. $this->targetDb->commit();
  87. $this->log('Import completed successfully');
  88. return ['success' => true, 'results' => $results, 'log' => $this->importLog];
  89. } catch (Exception $e) {
  90. $this->targetDb->rollBack();
  91. $this->errors[] = $e->getMessage();
  92. $this->log('Import failed: ' . $e->getMessage(), 'error');
  93. return ['success' => false, 'error' => $e->getMessage(), 'log' => $this->importLog, 'errors' => $this->errors];
  94. }
  95. }
  96. /**
  97. * Import WordPress categories
  98. */
  99. public function importCategories() {
  100. $this->log('Starting categories import');
  101. $imported = 0;
  102. $skipped = 0;
  103. // Get WordPress categories
  104. $stmt = $this->wpDb->query("
  105. SELECT t.name, tt.description, tt.term_id
  106. FROM wp_terms t
  107. JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  108. WHERE tt.taxonomy = 'category'
  109. ORDER BY t.name
  110. ");
  111. $categories = $stmt->fetchAll();
  112. foreach ($categories as $wpCategory) {
  113. try {
  114. // Check if category already exists
  115. $existing = $this->targetDb->fetch(
  116. "SELECT id FROM categories WHERE name = ?",
  117. [$wpCategory['name']]
  118. );
  119. if ($existing) {
  120. $skipped++;
  121. $this->log("Category '{$wpCategory['name']}' already exists, skipping");
  122. continue;
  123. }
  124. // Insert new category
  125. $this->targetDb->execute(
  126. "INSERT INTO categories (name, description, created_at) VALUES (?, ?, NOW())",
  127. [
  128. $wpCategory['name'],
  129. $wpCategory['description'] ?? ''
  130. ]
  131. );
  132. $imported++;
  133. $this->log("Imported category: {$wpCategory['name']}");
  134. } catch (Exception $e) {
  135. $this->errors[] = "Error importing category '{$wpCategory['name']}': " . $e->getMessage();
  136. $this->log("Error importing category '{$wpCategory['name']}': " . $e->getMessage(), 'error');
  137. }
  138. }
  139. $this->log("Categories import completed: {$imported} imported, {$skipped} skipped");
  140. return ['imported' => $imported, 'skipped' => $skipped];
  141. }
  142. /**
  143. * Import WordPress users
  144. */
  145. public function importUsers() {
  146. $this->log('Starting users import');
  147. $imported = 0;
  148. $skipped = 0;
  149. // Get WordPress users
  150. $stmt = $this->wpDb->query("
  151. SELECT ID, user_login, user_email, user_nicename, user_registered, display_name
  152. FROM wp_users
  153. WHERE user_status = 0
  154. ORDER BY ID
  155. ");
  156. $users = $stmt->fetchAll();
  157. foreach ($users as $wpUser) {
  158. try {
  159. // Check if user already exists
  160. $existing = $this->targetDb->fetch(
  161. "SELECT id FROM users WHERE username = ?",
  162. [$wpUser['user_login']]
  163. );
  164. if ($existing) {
  165. $skipped++;
  166. $this->log("User '{$wpUser['user_login']}' already exists, skipping");
  167. continue;
  168. }
  169. // Determine user role (WordPress usermeta table)
  170. $role = $this->getUserRole($wpUser['ID']);
  171. // Insert new user
  172. $this->targetDb->execute(
  173. "INSERT INTO users (username, email, role, auth_type, created_at) VALUES (?, ?, ?, 'wordpress', ?)",
  174. [
  175. $wpUser['user_login'],
  176. $wpUser['user_email'],
  177. $role,
  178. $wpUser['user_registered']
  179. ]
  180. );
  181. $imported++;
  182. $this->log("Imported user: {$wpUser['user_login']} (role: {$role})");
  183. } catch (Exception $e) {
  184. $this->errors[] = "Error importing user '{$wpUser['user_login']}': " . $e->getMessage();
  185. $this->log("Error importing user '{$wpUser['user_login']}': " . $e->getMessage(), 'error');
  186. }
  187. }
  188. $this->log("Users import completed: {$imported} imported, {$skipped} skipped");
  189. return ['imported' => $imported, 'skipped' => $skipped];
  190. }
  191. /**
  192. * Import WordPress posts
  193. */
  194. public function importPosts() {
  195. $this->log('Starting posts import');
  196. $imported = 0;
  197. $skipped = 0;
  198. // Get WordPress posts (only published posts, not pages)
  199. $stmt = $this->wpDb->query("
  200. SELECT p.ID, p.post_title, p.post_content, p.post_excerpt, p.post_date,
  201. p.post_modified, p.post_status, p.post_author, p.post_name
  202. FROM wp_posts p
  203. WHERE p.post_type = 'post' AND p.post_status IN ('publish', 'draft')
  204. ORDER BY p.post_date
  205. ");
  206. $posts = $stmt->fetchAll();
  207. foreach ($posts as $wpPost) {
  208. try {
  209. // Get author name
  210. $author = $this->getAuthorName($wpPost['post_author']);
  211. // Get post categories
  212. $categories = $this->getPostCategories($wpPost['ID']);
  213. // Generate slug from post_name or title
  214. $slug = !empty($wpPost['post_name']) ? $wpPost['post_name'] : $this->generateSlug($wpPost['post_title']);
  215. // Map WordPress status to our status
  216. $status = ($wpPost['post_status'] === 'publish') ? 'published' : 'draft';
  217. // Insert post
  218. $this->targetDb->execute(
  219. "INSERT INTO publications (title, slug, content, summary, author, status, created_at, updated_at, published_at)
  220. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
  221. [
  222. $wpPost['post_title'],
  223. $slug,
  224. $this->processContent($wpPost['post_content']),
  225. $wpPost['post_excerpt'] ?? '',
  226. $author,
  227. $status,
  228. $wpPost['post_date'],
  229. $wpPost['post_modified'],
  230. ($status === 'published') ? $wpPost['post_date'] : null
  231. ]
  232. );
  233. $publicationId = $this->targetDb->lastInsertId();
  234. // Link categories
  235. if (!empty($categories)) {
  236. $this->linkPostCategories($publicationId, $categories);
  237. }
  238. $imported++;
  239. $this->log("Imported post: '{$wpPost['post_title']}'");
  240. } catch (Exception $e) {
  241. $this->errors[] = "Error importing post '{$wpPost['post_title']}': " . $e->getMessage();
  242. $this->log("Error importing post '{$wpPost['post_title']}': " . $e->getMessage(), 'error');
  243. }
  244. }
  245. $this->log("Posts import completed: {$imported} imported, {$skipped} skipped");
  246. return ['imported' => $imported, 'skipped' => $skipped];
  247. }
  248. /**
  249. * Import WordPress comments
  250. */
  251. public function importComments() {
  252. $this->log('Starting comments import');
  253. $imported = 0;
  254. $skipped = 0;
  255. // Get WordPress comments
  256. $stmt = $this->wpDb->query("
  257. SELECT c.comment_ID, c.comment_post_ID, c.comment_author, c.comment_author_email,
  258. c.comment_content, c.comment_date, c.comment_approved, c.comment_parent
  259. FROM wp_comments c
  260. JOIN wp_posts p ON c.comment_post_ID = p.ID
  261. WHERE p.post_type = 'post'
  262. ORDER BY c.comment_date
  263. ");
  264. $comments = $stmt->fetchAll();
  265. foreach ($comments as $wpComment) {
  266. try {
  267. // Find corresponding publication
  268. $publication = $this->targetDb->fetch(
  269. "SELECT id FROM publications WHERE slug = ? OR title = ? LIMIT 1",
  270. [$this->getPostSlugById($wpComment['comment_post_ID']), $this->getPostTitleById($wpComment['comment_post_ID'])]
  271. );
  272. if (!$publication) {
  273. $skipped++;
  274. $this->log("Comment skipped - no matching publication found for post ID {$wpComment['comment_post_ID']}");
  275. continue;
  276. }
  277. // Map comment status
  278. $status = ($wpComment['comment_approved'] === '1') ? 'approved' : 'pending';
  279. // Handle parent comment
  280. $parentId = null;
  281. if ($wpComment['comment_parent'] > 0) {
  282. $parentComment = $this->targetDb->fetch(
  283. "SELECT id FROM comments WHERE wp_comment_id = ?",
  284. [$wpComment['comment_parent']]
  285. );
  286. if ($parentComment) {
  287. $parentId = $parentComment['id'];
  288. }
  289. }
  290. // Insert comment
  291. $this->targetDb->execute(
  292. "INSERT INTO comments (publication_id, parent_id, name, email, content, status, created_at, admin_reply)
  293. VALUES (?, ?, ?, ?, ?, ?, ?, FALSE)",
  294. [
  295. $publication['id'],
  296. $parentId,
  297. $wpComment['comment_author'],
  298. $wpComment['comment_author_email'],
  299. $wpComment['comment_content'],
  300. $status,
  301. $wpComment['comment_date']
  302. ]
  303. );
  304. $commentId = $this->targetDb->lastInsertId();
  305. // Store WordPress comment ID for parent mapping
  306. $this->targetDb->execute(
  307. "UPDATE comments SET wp_comment_id = ? WHERE id = ?",
  308. [$wpComment['comment_ID'], $commentId]
  309. );
  310. $imported++;
  311. $this->log("Imported comment for post ID {$wpComment['comment_post_ID']}");
  312. } catch (Exception $e) {
  313. $this->errors[] = "Error importing comment: " . $e->getMessage();
  314. $this->log("Error importing comment: " . $e->getMessage(), 'error');
  315. }
  316. }
  317. $this->log("Comments import completed: {$imported} imported, {$skipped} skipped");
  318. return ['imported' => $imported, 'skipped' => $skipped];
  319. }
  320. /**
  321. * Helper methods
  322. */
  323. private function getUserRole($userId) {
  324. $stmt = $this->wpDb->prepare("
  325. SELECT meta_value FROM wp_usermeta
  326. WHERE user_id = ? AND meta_key = 'wp_capabilities'
  327. ");
  328. $stmt->execute([$userId]);
  329. $capabilities = $stmt->fetchColumn();
  330. if ($capabilities && strpos($capabilities, 'administrator') !== false) {
  331. return 'admin';
  332. }
  333. return 'editor'; // Default role
  334. }
  335. private function getAuthorName($authorId) {
  336. $stmt = $this->wpDb->prepare("SELECT display_name FROM wp_users WHERE ID = ?");
  337. $stmt->execute([$authorId]);
  338. $name = $stmt->fetchColumn();
  339. return $name ?: 'Unknown Author';
  340. }
  341. private function getPostCategories($postId) {
  342. $stmt = $this->wpDb->prepare("
  343. SELECT t.name FROM wp_terms t
  344. JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
  345. JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
  346. WHERE tr.object_id = ? AND tt.taxonomy = 'category'
  347. ");
  348. $stmt->execute([$postId]);
  349. return $stmt->fetchAll(PDO::FETCH_COLUMN);
  350. }
  351. private function getPostSlugById($postId) {
  352. $stmt = $this->wpDb->prepare("SELECT post_name FROM wp_posts WHERE ID = ?");
  353. $stmt->execute([$postId]);
  354. return $stmt->fetchColumn() ?: '';
  355. }
  356. private function getPostTitleById($postId) {
  357. $stmt = $this->wpDb->prepare("SELECT post_title FROM wp_posts WHERE ID = ?");
  358. $stmt->execute([$postId]);
  359. return $stmt->fetchColumn() ?: '';
  360. }
  361. private function linkPostCategories($publicationId, $categories) {
  362. foreach ($categories as $categoryName) {
  363. $category = $this->targetDb->fetch(
  364. "SELECT id FROM categories WHERE name = ?",
  365. [$categoryName]
  366. );
  367. if ($category) {
  368. $this->targetDb->execute(
  369. "INSERT IGNORE INTO publication_categories (publication_id, category_id) VALUES (?, ?)",
  370. [$publicationId, $category['id']]
  371. );
  372. }
  373. }
  374. }
  375. private function processContent($content) {
  376. // Basic WordPress content processing
  377. // You can extend this to handle shortcodes, etc.
  378. $content = str_replace('[caption]', '', $content);
  379. $content = str_replace('[/caption]', '', $content);
  380. $content = preg_replace('/\[gallery.*?\]/', '', $content);
  381. return $content;
  382. }
  383. private function generateSlug($title) {
  384. $slug = strtolower($title);
  385. $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
  386. $slug = trim($slug, '-');
  387. return $slug;
  388. }
  389. private function log($message, $level = 'info') {
  390. $this->importLog[] = [
  391. 'timestamp' => date('Y-m-d H:i:s'),
  392. 'level' => $level,
  393. 'message' => $message
  394. ];
  395. }
  396. /**
  397. * Get statistics methods
  398. */
  399. public function getPostCount() {
  400. $stmt = $this->wpDb->query("SELECT COUNT(*) FROM wp_posts WHERE post_type = 'post'");
  401. return $stmt->fetchColumn();
  402. }
  403. public function getPageCount() {
  404. $stmt = $this->wpDb->query("SELECT COUNT(*) FROM wp_posts WHERE post_type = 'page'");
  405. return $stmt->fetchColumn();
  406. }
  407. public function getCategoryCount() {
  408. $stmt = $this->wpDb->query("
  409. SELECT COUNT(*) FROM wp_term_taxonomy
  410. WHERE taxonomy = 'category'
  411. ");
  412. return $stmt->fetchColumn();
  413. }
  414. public function getUserCount() {
  415. $stmt = $this->wpDb->query("SELECT COUNT(*) FROM wp_users");
  416. return $stmt->fetchColumn();
  417. }
  418. public function getCommentCount() {
  419. $stmt = $this->wpDb->query("SELECT COUNT(*) FROM wp_comments");
  420. return $stmt->fetchColumn();
  421. }
  422. /**
  423. * Get import log
  424. */
  425. public function getLog() {
  426. return $this->importLog;
  427. }
  428. /**
  429. * Get errors
  430. */
  431. public function getErrors() {
  432. return $this->errors;
  433. }
  434. }