SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/ImportCMS.php

  1. <?php
  2. namespace Sprout\Helpers;
  3.  
  4. use DOMDocument;
  5. use SimpleXMLElement;
  6. use Exception;
  7.  
  8. use Sprout\Helpers\Enc;
  9. use Sprout\Helpers\Notification;
  10. use Sprout\Helpers\Pdb;
  11. use Sprout\Helpers\Slug;
  12. use Sprout\Helpers\WidgetArea;
  13.  
  14.  
  15. class ImportCMS
  16. {
  17. private static $page_ids = [];
  18. private static $revision_ids = [];
  19. private static $old_widets = [];
  20.  
  21.  
  22. /**
  23.   * Process given XML file into Sprout CMS 3 pages
  24.   *
  25.   * @param string $filename Path and filename of XML
  26.   * @return array
  27.   */
  28. public static function import($filename)
  29. {
  30. // Load XML document
  31. $xml = new SimpleXMLElement(file_get_contents($filename), LIBXML_NOCDATA);
  32. if (!$xml) throw new Exception('Unable to parse XML');
  33.  
  34. Pdb::transact();
  35.  
  36. // Slug conditions
  37. $conds = [
  38. 'subsite_id' => $_POST['subsite_id'],
  39. 'parent_id' => $_POST['page_id']
  40. ];
  41.  
  42. // Fetch record order
  43. $q = "SELECT
  44. MAX(page.record_order) AS o
  45. FROM
  46. ~pages AS page
  47. WHERE
  48. page.parent_id = ?";
  49.  
  50. $record_order = Pdb::query($q, [$_POST['page_id']], 'val');
  51. $record_order ++;
  52.  
  53. // Create pages and content widgets
  54. foreach ($xml->page as $page) {
  55. self::processXmlPage($page, $record_order ++, $_POST['page_id'], $_POST['subsite_id']);
  56. }
  57.  
  58. self::updateContent();
  59.  
  60. Pdb::commit();
  61.  
  62. $results = [];
  63. foreach (self::$page_ids as $old_id => $new_id) {
  64. $results[] = [
  65. 'old_id' => $old_id,
  66. 'new_id' => $new_id,
  67. 'widgets' => @self::$old_widets[$old_id],
  68. ];
  69. }
  70.  
  71. return $results;
  72. }
  73.  
  74.  
  75. /**
  76.   * Process DOMElement into page record with content
  77.   *
  78.   * @param DomElement $page
  79.   * @param int $record_order
  80.   * @param int $parent_id
  81.   * @param int $subsite_id
  82.   * @param int $depth Number of recursions
  83.   * @return void
  84.   */
  85. private static function processXmlPage($page, $record_order, $parent_id, $subsite_id)
  86. {
  87. // Create page record
  88. $fields = [];
  89. $fields['parent_id'] = $parent_id;
  90. $fields['subsite_id'] = $subsite_id;
  91. $fields['record_order'] = $record_order ++;
  92. $fields['name'] = trim((string) $page['name']);
  93. $fields['active'] = ((string)$page['active'] ? 1 : 0);
  94. $fields['show_in_nav'] = ((string)$page['menu'] ? 1 : 0);
  95. $fields['menu_group'] = (int) !empty($page['menu-group-position'])? (string) $page['menu-group-position'] : 0;
  96. $fields['modified_editor'] = 'Import tool';
  97. $fields['date_added'] = Pdb::now();
  98. $fields['date_modified'] = Pdb::now();
  99.  
  100. try {
  101. $fields['slug'] = Slug::unique(Enc::urlname($fields['name'], '-'), 'pages', $conds);
  102. } catch (Exception $ex) {
  103. $fields['slug'] = Slug::create('pages', $fields['name']);
  104. }
  105.  
  106. $page_id = Pdb::insert('pages', $fields);
  107.  
  108. // Map old page ID to new page ID
  109. self::$page_ids[(int) $page['id']] = $page_id;
  110.  
  111. // Add first revision
  112. $fields = [];
  113. $fields['page_id'] = $page_id;
  114. $fields['type'] = 'standard';
  115. $fields['status'] = 'live';
  116. $fields['changes_made'] = 'Import of existing content';
  117. $fields['modified_editor'] = 'Import tool';
  118. $fields['date_added'] = Pdb::now();
  119. $fields['date_modified'] = Pdb::now();
  120.  
  121. $revision_id = Pdb::insert('page_revisions', $fields);
  122.  
  123. // Map old page ID to new page ID
  124. self::$revision_ids[(int) $page['id']] = $revision_id;
  125.  
  126. // Content
  127. $html = (string) $page->content[0];
  128.  
  129. // Widget area
  130. $area = WidgetArea::findAreaByName('embedded');
  131.  
  132. // RichText widget
  133. $fields = [];
  134. $fields['page_revision_id'] = $revision_id;
  135. $fields['area_id'] = $area->getIndex();
  136. $fields['active'] = 1;
  137. $fields['record_order'] = 0;
  138. $fields['type'] = 'RichText';
  139. $fields['settings'] = json_encode(['text' => $html]);
  140.  
  141. Pdb::insert('page_widgets', $fields);
  142.  
  143. if (empty($page->children->page)) return;
  144.  
  145. $record_order = 1;
  146. foreach ($page->children->page as $child_page) {
  147. self::processXmlPage($child_page, $record_order++, $page_id, $subsite_id);
  148. }
  149. }
  150.  
  151.  
  152. /**
  153.   * Update content for Sprout CMS 3
  154.   *
  155.   * @return void
  156.   */
  157. private static function updateContent()
  158. {
  159. $params = [];
  160. $conditions = [];
  161.  
  162. $conditions[] = ['widget.page_revision_id', 'IN', self::$revision_ids];
  163. $where = Pdb::buildClause($conditions, $params);
  164.  
  165. $q = "SELECT
  166. widget.id,
  167. widget.page_revision_id,
  168. widget.settings
  169. FROM
  170. ~page_widgets AS widget
  171. WHERE
  172. {$where}";
  173.  
  174. $widgets = Pdb::query($q, $params, 'arr');
  175.  
  176. foreach ($widgets as $widget) {
  177. $settings = json_decode($widget['settings']);
  178.  
  179. $settings->text = self::replacePageIds($settings->text);
  180. $settings->text = self::replaceFileUrls($settings->text);
  181.  
  182. self::findOldWidgets($widget['page_revision_id'], $settings->text);
  183.  
  184. Pdb::update('page_widgets', ['settings' => json_encode(['text' => $settings->text])], [['id', '=', $widget['id']]]);
  185. }
  186. }
  187.  
  188.  
  189. /**
  190.   * Replace page IDs within page URLS
  191.   *
  192.   * @param string $html HTML content
  193.   * @return string HTML with replaced URLs
  194.   */
  195. private static function replacePageIds($html)
  196. {
  197. return preg_replace_callback('!href="page/view_by_id/([0-9]+)"!', function ($matches)
  198. {
  199. if (isset(self::$page_ids[$matches[1]])) return 'href="page/view_by_id/' . self::$page_ids[$matches[1]] . '"';
  200. return 'href="unknown_page_' . $matches[1] . '"';
  201. }, $html);
  202. }
  203.  
  204.  
  205. /**
  206.   * Report pages that contain old widgets
  207.   *
  208.   * @param string $html HTML content
  209.   * @return void
  210.   */
  211. private static function findOldWidgets($rev_id, $html)
  212. {
  213. $matches = [];
  214. '/\(\(WIDGET [a-zA-Z]*? ?([0-9A-Za-z]+)\)\)/',
  215. $html,
  216. $matches
  217. );
  218.  
  219. if (empty($matches[0]) or count($matches[0]) == 0) return;
  220.  
  221. self::$old_widets[array_search($rev_id, self::$revision_ids)] = implode(' ~ ', $matches[0]);
  222. }
  223.  
  224.  
  225. /**
  226.   * Replace file URLs
  227.   * FROM: files/1702_icon_joint.small.png
  228.   * TO: file/download/1702/small
  229.   *
  230.   *
  231.   * @param string $html HTML content
  232.   * @return string HTML with replaced file URLs
  233.   */
  234. private static function replaceFileUrls($html)
  235. {
  236. return preg_replace_callback('!(src|href)="files/([0-9]+)_[_.0-9A-Za-z]+"!', function($matches)
  237. {
  238. $url = $matches[1] . '="file/download/' . $matches[2];
  239.  
  240. if (strpos($matches[0], '.small.') !== false) {
  241. $url .= '/small';
  242. } else if (strpos($matches[0], '.medium.') !== false) {
  243. $url .= '/medium';
  244. } else if (strpos($matches[0], '.large.') !== false) {
  245. $url .= '/large';
  246. }
  247.  
  248. return $url . '"';
  249.  
  250. }, $html);
  251. }
  252. }
  253.