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