1<?php 2 3namespace plugin\struct\meta; 4 5use plugin\struct\types\AbstractBaseType; 6use plugin\struct\types\Text; 7 8class Schema { 9 10 /** @var \helper_plugin_sqlite|null */ 11 protected $sqlite; 12 13 /** @var int The ID of this schema */ 14 protected $id = 0; 15 16 /** @var string name of the associated table */ 17 protected $table = ''; 18 19 /** 20 * @var string the current checksum of this schema 21 */ 22 protected $chksum = ''; 23 24 /** @var AbstractBaseType[] all the colums */ 25 protected $columns = array(); 26 27 /** @var int */ 28 protected $maxsort = 0; 29 30 /** 31 * Schema constructor 32 * @param string $table The table this schema is for 33 * @param int $ts The timestamp for when this schema was valid, 0 for current 34 */ 35 public function __construct($table, $ts = 0) { 36 /** @var \helper_plugin_struct_db $helper */ 37 $helper = plugin_load('helper', 'struct_db'); 38 $this->sqlite = $helper->getDB(); 39 if(!$this->sqlite) return; 40 41 $table = self::cleanTableName($table); 42 $this->table = $table; 43 44 // load info about the schema itself 45 if($ts) { 46 $sql = "SELECT * 47 FROM schemas 48 WHERE tbl = ? 49 AND ts <= ? 50 ORDER BY ts DESC 51 LIMIT 1"; 52 $opt = array($table, $ts); 53 54 } else { 55 $sql = "SELECT * 56 FROM schemas 57 WHERE tbl = ? 58 ORDER BY ts DESC 59 LIMIT 1"; 60 $opt = array($table); 61 } 62 $res = $this->sqlite->query($sql, $opt); 63 if($this->sqlite->res2count($res)) { 64 $result = $this->sqlite->res2arr($res); 65 $this->id = $result['id']; 66 $this->chksum = $result['chksum']; 67 68 } 69 $this->sqlite->res_close($res); 70 if(!$this->id) return; 71 72 // load existing columns 73 $sql = "SELECT SC.*, T.* 74 FROM schema_cols SC, 75 types T 76 WHERE SC.schema_id = ? 77 AND SC.type_id = T.id 78 ORDER BY SC.sort"; 79 $res = $this->sqlite->query($sql, $opt); 80 $rows = $this->sqlite->res2arr($res); 81 $this->sqlite->res_close($res); 82 83 foreach($rows as $row) { 84 $class = 'plugin\\struct\\type\\' . $row['class']; 85 $config = json_decode($row['config'], true); 86 $this->columns[$row['col']] = new $class($config, $row['label'], $row['ismulti']); 87 if($row['sort'] > $this->maxsort) $this->maxsort = $row['sort']; 88 } 89 } 90 91 /** 92 * Cleans any unwanted stuff from table names 93 * 94 * @param string $table 95 * @return string 96 */ 97 static public function cleanTableName($table) { 98 $table = preg_replace('/[^a-z0-9_]+/', '', $table); 99 $table = preg_replace('/^[0-9_]+/', '', $table); 100 $table = trim($table); 101 return $table; 102 } 103 104 /** 105 * Returns a table to edit the schema 106 * 107 * @todo should this include the form? 108 * 109 * @return string 110 */ 111 public function adminEditor() { 112 $html = ''; 113 114 $html .= '<input type="hidden" name="table" value="' . hsc($this->table) . '">'; 115 116 $html .= '<table class="inline">'; 117 $html .= '<tr><th>Sort</th><th>Label</th><th>Multi-Input?</th><th>Configuration</th><th>Type</th></tr>'; // FIXME localize 118 foreach($this->columns as $key => $obj) { 119 $html .= $this->adminColumn($key, $obj); 120 } 121 122 // FIXME new one needs to be added dynamically, this is just for testing 123 $html .= $this->adminColumn('new1', new Text($this->maxsort+10)); 124 125 $html .= '</table>'; 126 return $html; 127 } 128 129 /** 130 * Returns the HTML to edit a single column definition of the schema 131 * 132 * @param string $column_id 133 * @param AbstractBaseType $type 134 * @return string 135 * @todo this should probably be reused for adding new columns via AJAX later? 136 */ 137 protected function adminColumn($column_id, AbstractBaseType $type) { 138 $base = 'schema[' . $column_id . ']'; // base name for all fields 139 140 $html = '<tr>'; 141 142 $html .= '<td>'; 143 $html .= '<input type="text" name="' . $base . '[sort]" value="' . hsc($type->getSort()) . '" size="3">'; 144 $html .= '</td>'; 145 146 $html .= '<td>'; 147 $html .= '<input type="text" name="' . $base . '[label]" value="' . hsc($type->getLabel()) . '">'; 148 $html .= '</td>'; 149 150 $html .= '<td>'; 151 $checked = $type->isMulti() ? 'checked="checked"' : ''; 152 $html .= '<input type="checkbox" name="' . $base . '[ismulti]" value="1" ' . $checked . '>'; 153 $html .= '</td>'; 154 155 $html .= '<td>'; 156 $config = json_encode($type->getConfig(), JSON_PRETTY_PRINT); 157 $html .= '<textarea name="' . $base . '[config]" cols="45" rows="10">' . hsc($config) . '</textarea>'; 158 $html .= '</td>'; 159 160 $html .= '<td>'; 161 $html .= substr(get_class($type), 20); //FIXME this needs to be a dropdown 162 $html .= '</td>'; 163 164 $html .= '</tr>'; 165 166 return $html; 167 } 168 169} 170