SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/Preview.php

  1. <?php
  2. /*
  3.  * Copyright (C) 2017 Karmabunny Pty Ltd.
  4.  *
  5.  * This file is a part of SproutCMS.
  6.  *
  7.  * SproutCMS is free software: you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation, either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * For more information, visit <http://getsproutcms.com>.
  12.  */
  13.  
  14. namespace Sprout\Helpers;
  15.  
  16. use InvalidArgumentException;
  17.  
  18. use Sprout\Controllers\Admin\ManagedAdminController;
  19. use Sprout\Controllers\Controller;
  20.  
  21.  
  22. /**
  23.  * Somewhat complicated preview system which copies existing data into
  24.  * temporary tables, then renders the preview via a front-end method call
  25.  */
  26. class Preview {
  27.  
  28. /**
  29.   * Loads a preview by creating temporary tables in SQL, calling a
  30.   * controller's _editSave method, and then calling a method it would
  31.   * normally use to display its (non-temporary) data
  32.   *
  33.   * @param ManagedAdminController $ctlr The controller with the preview method
  34.   * @param array $tables Tables to copy. The keys are the table names, and
  35.   * the values are WHERE clauses for data to copy across: use 0 to
  36.   * copy just the structure with no data, and 1 to copy all data;
  37.   * otherwise use an array of conditions (see {@see Pdb::buildClause})
  38.   * The first table specified is the 'core' table which will contain
  39.   * the record which will be previewed.
  40.   * N.B. It's unnecessary to specify pages, page_revisions, or
  41.   * menu_groups here; they will be included automatically.
  42.   * @param int $record_id ID of the main record, if it exists; 0 to insert a new record
  43.   * @return int The ID of the record (whether existing or newly inserted)
  44.   */
  45. public static function load(ManagedAdminController $ctlr, array $tables, $record_id = 0)
  46. {
  47. // Tables that are always required for the main nav to work
  48. $subsite = (int) $_SESSION['admin']['active_subsite'];
  49. $defaults = array(
  50. 'pages' => 'parent_id = 0 AND subsite_id = ' . $subsite,
  51. 'page_revisions' => "status = 'live'",
  52. 'menu_groups' => 'subsite_id = ' . $subsite,
  53. );
  54. foreach ($defaults as $table => $where) {
  55. if (!isset($tables[$table])) $tables[$table] = $where;
  56. }
  57.  
  58. // Generate new prefix for temporary tables
  59. $rand = Sprout::randStr(8);
  60. $old_pf = Pdb::prefix();
  61. $new_pf = "preview_{$rand}_";
  62.  
  63. foreach ($tables as $table => $conditions) {
  64. Pdb::validateIdentifier($table);
  65. if ($conditions == 0) {
  66. $conditions = [0];
  67. } else if ($conditions == 1) {
  68. $conditions = [1];
  69. } else if (!is_array($conditions)) {
  70. throw new InvalidArgumentException('Conditions must be 1, 0, or an array');
  71. }
  72.  
  73. $new = "{$new_pf}{$table}";
  74. $old = "{$old_pf}{$table}";
  75.  
  76. $params = [];
  77. $where = Pdb::buildClause($conditions, $params);
  78.  
  79. // Ideally this should be CREATE TEMPORARY TABLE ... LIKE ...
  80. // But it's unuseable due to a MySQL (5.1?) bug - see e.g.
  81. // https://bugs.mysql.com/bug.php?id=60574
  82. $q = "CREATE TEMPORARY TABLE {$new} SELECT * FROM {$old} WHERE {$where}";
  83. Pdb::q($q, $params, 'null');
  84.  
  85. Pdb::setTablePrefixOverride($table, $new_pf);
  86. }
  87.  
  88. $table_names = array_keys($tables);
  89. $core_table = reset($table_names);
  90.  
  91. // The CREATE TEMPORARY TABLE ... SELECT above means there's no AUTO_INCREMENT on the id column,
  92. // so add one before inserting a new record
  93. if ($record_id <= 0) {
  94. $q = "ALTER TABLE ~{$core_table} MODIFY COLUMN id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY";
  95. Pdb::q($q, [], 'null');
  96. $record_id = Pdb::insert($core_table, ['id' => 0]);
  97. }
  98.  
  99. $ctlr->_editSave($record_id);
  100.  
  101. // Edit save has likely set these during validation
  102. // Remove them to avoid weird behaviour
  103. unset($_SESSION['admin']['field_values']);
  104. unset($_SESSION['admin']['field_errors']);
  105.  
  106. return $record_id;
  107. }
  108.  
  109.  
  110. /**
  111.   * Run a controller method using the preview data.
  112.   * This injects the word 'PREVIEW' into the page title
  113.   *
  114.   * @param Controller $ctlr
  115.   * @param string $method The method to run
  116.   * @param array $args Arguments for the method
  117.   * @return void This calls echo
  118.   */
  119. public static function run(Controller $ctlr, $method, array $args = [])
  120. {
  121. call_user_func_array([$ctlr, $method], $args);
  122. $content = ob_get_clean();
  123.  
  124. '/<title[^>]*?>(.*?)<\/title>/i',
  125. '<title>PREVIEW - $1</title>',
  126. $content
  127. );
  128. }
  129.  
  130. }
  131.