1083afc55SAndreas Gohr<?php 2083afc55SAndreas Gohr 3083afc55SAndreas Gohrnamespace plugin\struct\meta; 4083afc55SAndreas Gohr 51c502704SAndreas Gohruse dokuwiki\Form\Form; 6083afc55SAndreas Gohruse plugin\struct\types\AbstractBaseType; 7083afc55SAndreas Gohruse plugin\struct\types\Text; 8083afc55SAndreas Gohr 9*7182938bSAndreas Gohr/** 10*7182938bSAndreas Gohr * Class Schema 11*7182938bSAndreas Gohr * 12*7182938bSAndreas Gohr * Represents the schema of a single data table and all its properties. It defines what can be stored in 13*7182938bSAndreas Gohr * the represented data table and how those contents are formatted. 14*7182938bSAndreas Gohr * 15*7182938bSAndreas Gohr * It can be initialized with a timestamp to access the schema as it looked at that particular point in time. 16*7182938bSAndreas Gohr * 17*7182938bSAndreas Gohr * @package plugin\struct\meta 18*7182938bSAndreas Gohr */ 19083afc55SAndreas Gohrclass Schema { 20083afc55SAndreas Gohr 21083afc55SAndreas Gohr /** @var \helper_plugin_sqlite|null */ 22083afc55SAndreas Gohr protected $sqlite; 23083afc55SAndreas Gohr 24083afc55SAndreas Gohr /** @var int The ID of this schema */ 25083afc55SAndreas Gohr protected $id = 0; 26083afc55SAndreas Gohr 27083afc55SAndreas Gohr /** @var string name of the associated table */ 28083afc55SAndreas Gohr protected $table = ''; 29083afc55SAndreas Gohr 30083afc55SAndreas Gohr /** 31083afc55SAndreas Gohr * @var string the current checksum of this schema 32083afc55SAndreas Gohr */ 33083afc55SAndreas Gohr protected $chksum = ''; 34083afc55SAndreas Gohr 351c502704SAndreas Gohr /** @var Column[] all the colums */ 36083afc55SAndreas Gohr protected $columns = array(); 37083afc55SAndreas Gohr 38083afc55SAndreas Gohr /** @var int */ 39083afc55SAndreas Gohr protected $maxsort = 0; 40083afc55SAndreas Gohr 41083afc55SAndreas Gohr /** 42083afc55SAndreas Gohr * Schema constructor 43*7182938bSAndreas Gohr * 44083afc55SAndreas Gohr * @param string $table The table this schema is for 45083afc55SAndreas Gohr * @param int $ts The timestamp for when this schema was valid, 0 for current 46083afc55SAndreas Gohr */ 47083afc55SAndreas Gohr public function __construct($table, $ts = 0) { 48083afc55SAndreas Gohr /** @var \helper_plugin_struct_db $helper */ 49083afc55SAndreas Gohr $helper = plugin_load('helper', 'struct_db'); 50083afc55SAndreas Gohr $this->sqlite = $helper->getDB(); 51083afc55SAndreas Gohr if(!$this->sqlite) return; 52083afc55SAndreas Gohr 53083afc55SAndreas Gohr $table = self::cleanTableName($table); 54083afc55SAndreas Gohr $this->table = $table; 55083afc55SAndreas Gohr 56083afc55SAndreas Gohr // load info about the schema itself 57083afc55SAndreas Gohr if($ts) { 58083afc55SAndreas Gohr $sql = "SELECT * 59083afc55SAndreas Gohr FROM schemas 60083afc55SAndreas Gohr WHERE tbl = ? 61083afc55SAndreas Gohr AND ts <= ? 62083afc55SAndreas Gohr ORDER BY ts DESC 63083afc55SAndreas Gohr LIMIT 1"; 64083afc55SAndreas Gohr $opt = array($table, $ts); 65083afc55SAndreas Gohr 66083afc55SAndreas Gohr } else { 67083afc55SAndreas Gohr $sql = "SELECT * 68083afc55SAndreas Gohr FROM schemas 69083afc55SAndreas Gohr WHERE tbl = ? 70083afc55SAndreas Gohr ORDER BY ts DESC 71083afc55SAndreas Gohr LIMIT 1"; 72083afc55SAndreas Gohr $opt = array($table); 73083afc55SAndreas Gohr } 74083afc55SAndreas Gohr $res = $this->sqlite->query($sql, $opt); 75083afc55SAndreas Gohr if($this->sqlite->res2count($res)) { 761c502704SAndreas Gohr $result = array_shift($this->sqlite->res2arr($res)); 77083afc55SAndreas Gohr $this->id = $result['id']; 78083afc55SAndreas Gohr $this->chksum = $result['chksum']; 79083afc55SAndreas Gohr 80083afc55SAndreas Gohr } 81083afc55SAndreas Gohr $this->sqlite->res_close($res); 82083afc55SAndreas Gohr if(!$this->id) return; 83083afc55SAndreas Gohr 84083afc55SAndreas Gohr // load existing columns 85083afc55SAndreas Gohr $sql = "SELECT SC.*, T.* 86083afc55SAndreas Gohr FROM schema_cols SC, 87083afc55SAndreas Gohr types T 881c502704SAndreas Gohr WHERE SC.sid = ? 891c502704SAndreas Gohr AND SC.tid = T.id 90083afc55SAndreas Gohr ORDER BY SC.sort"; 911c502704SAndreas Gohr $res = $this->sqlite->query($sql, $this->id); 92083afc55SAndreas Gohr $rows = $this->sqlite->res2arr($res); 93083afc55SAndreas Gohr $this->sqlite->res_close($res); 94083afc55SAndreas Gohr 95083afc55SAndreas Gohr foreach($rows as $row) { 961c502704SAndreas Gohr $class = 'plugin\\struct\\types\\' . $row['class']; 97083afc55SAndreas Gohr $config = json_decode($row['config'], true); 981c502704SAndreas Gohr $this->columns[$row['colref']] = 991c502704SAndreas Gohr new Column( 1001c502704SAndreas Gohr $row['sort'], 1011c502704SAndreas Gohr new $class($config, $row['label'], $row['ismulti']), 1021c502704SAndreas Gohr $row['tid'], 1031c502704SAndreas Gohr $row['colref'], 1041c502704SAndreas Gohr $row['enabled'] 1051c502704SAndreas Gohr ); 1061c502704SAndreas Gohr 107083afc55SAndreas Gohr if($row['sort'] > $this->maxsort) $this->maxsort = $row['sort']; 108083afc55SAndreas Gohr } 109083afc55SAndreas Gohr } 110083afc55SAndreas Gohr 111083afc55SAndreas Gohr /** 112083afc55SAndreas Gohr * Cleans any unwanted stuff from table names 113083afc55SAndreas Gohr * 114083afc55SAndreas Gohr * @param string $table 115083afc55SAndreas Gohr * @return string 116083afc55SAndreas Gohr */ 117083afc55SAndreas Gohr static public function cleanTableName($table) { 118083afc55SAndreas Gohr $table = preg_replace('/[^a-z0-9_]+/', '', $table); 119083afc55SAndreas Gohr $table = preg_replace('/^[0-9_]+/', '', $table); 120083afc55SAndreas Gohr $table = trim($table); 121083afc55SAndreas Gohr return $table; 122083afc55SAndreas Gohr } 123083afc55SAndreas Gohr 124083afc55SAndreas Gohr /** 1251c502704SAndreas Gohr * Returns the Admin Form to edit the schema 126083afc55SAndreas Gohr * 127*7182938bSAndreas Gohr * This data is processed by the SchemaBuilder class 128*7182938bSAndreas Gohr * 129083afc55SAndreas Gohr * @return string 130*7182938bSAndreas Gohr * @see SchemaBuilder 131*7182938bSAndreas Gohr * @todo it could be discussed if this editor should be part of the schema class it self or if that should be in a SchemaEditor class 132083afc55SAndreas Gohr */ 133083afc55SAndreas Gohr public function adminEditor() { 1341c502704SAndreas Gohr $form = new Form(array('method' => 'POST')); 1351c502704SAndreas Gohr $form->setHiddenField('do', 'admin'); 1361c502704SAndreas Gohr $form->setHiddenField('page', 'struct'); 1371c502704SAndreas Gohr $form->setHiddenField('table', $this->table); 1381c502704SAndreas Gohr $form->setHiddenField('schema[id]', $this->id); 139083afc55SAndreas Gohr 1401c502704SAndreas Gohr $form->addHTML('<table class="inline">'); 1411c502704SAndreas Gohr $form->addHTML('<tr><th>Sort</th><th>Label</th><th>Multi-Input?</th><th>Configuration</th><th>Type</th></tr>'); // FIXME localize 142083afc55SAndreas Gohr 143083afc55SAndreas Gohr foreach($this->columns as $key => $obj) { 1441c502704SAndreas Gohr $form->addHTML($this->adminColumn($key, $obj)); 145083afc55SAndreas Gohr } 146083afc55SAndreas Gohr 147083afc55SAndreas Gohr // FIXME new one needs to be added dynamically, this is just for testing 1481c502704SAndreas Gohr $form->addHTML($this->adminColumn('new1', new Column($this->maxsort+10, new Text()), 'new')); 149083afc55SAndreas Gohr 1501c502704SAndreas Gohr $form->addHTML('</table>'); 1511c502704SAndreas Gohr $form->addButton('save', 'Save')->attr('type','submit'); 1521c502704SAndreas Gohr return $form->toHTML(); 153083afc55SAndreas Gohr } 154083afc55SAndreas Gohr 155083afc55SAndreas Gohr /** 156083afc55SAndreas Gohr * Returns the HTML to edit a single column definition of the schema 157083afc55SAndreas Gohr * 158083afc55SAndreas Gohr * @param string $column_id 1591c502704SAndreas Gohr * @param Column $col 1601c502704SAndreas Gohr * @param string $key The key to use in the form 161083afc55SAndreas Gohr * @return string 162083afc55SAndreas Gohr * @todo this should probably be reused for adding new columns via AJAX later? 163*7182938bSAndreas Gohr * @todo as above this might be better fitted to a SchemaEditor class 164083afc55SAndreas Gohr */ 1651c502704SAndreas Gohr protected function adminColumn($column_id, Column $col, $key='cols') { 1661c502704SAndreas Gohr $base = 'schema['.$key.'][' . $column_id . ']'; // base name for all fields 167083afc55SAndreas Gohr 168083afc55SAndreas Gohr $html = '<tr>'; 169083afc55SAndreas Gohr 170083afc55SAndreas Gohr $html .= '<td>'; 1711c502704SAndreas Gohr $html .= '<input type="text" name="' . $base . '[sort]" value="' . hsc($col->getSort()) . '" size="3">'; 172083afc55SAndreas Gohr $html .= '</td>'; 173083afc55SAndreas Gohr 174083afc55SAndreas Gohr $html .= '<td>'; 1751c502704SAndreas Gohr $html .= '<input type="text" name="' . $base . '[label]" value="' . hsc($col->getType()->getLabel()) . '">'; 176083afc55SAndreas Gohr $html .= '</td>'; 177083afc55SAndreas Gohr 178083afc55SAndreas Gohr $html .= '<td>'; 1791c502704SAndreas Gohr $checked = $col->getType()->isMulti() ? 'checked="checked"' : ''; 180083afc55SAndreas Gohr $html .= '<input type="checkbox" name="' . $base . '[ismulti]" value="1" ' . $checked . '>'; 181083afc55SAndreas Gohr $html .= '</td>'; 182083afc55SAndreas Gohr 183083afc55SAndreas Gohr $html .= '<td>'; 1841c502704SAndreas Gohr $config = json_encode($col->getType()->getConfig(), JSON_PRETTY_PRINT); 185083afc55SAndreas Gohr $html .= '<textarea name="' . $base . '[config]" cols="45" rows="10">' . hsc($config) . '</textarea>'; 186083afc55SAndreas Gohr $html .= '</td>'; 187083afc55SAndreas Gohr 188083afc55SAndreas Gohr $html .= '<td>'; 1891c502704SAndreas Gohr $html .= '<input type="text" name="' . $base . '[class]" value="' . hsc($col->getType()->getClass()) . '">'; //FIXME this needs to be a dropdown 190083afc55SAndreas Gohr $html .= '</td>'; 191083afc55SAndreas Gohr 192083afc55SAndreas Gohr $html .= '</tr>'; 193083afc55SAndreas Gohr 194083afc55SAndreas Gohr return $html; 195083afc55SAndreas Gohr } 196083afc55SAndreas Gohr 1971c502704SAndreas Gohr /** 1981c502704SAndreas Gohr * @return string 1991c502704SAndreas Gohr */ 2001c502704SAndreas Gohr public function getChksum() { 2011c502704SAndreas Gohr return $this->chksum; 2021c502704SAndreas Gohr } 2031c502704SAndreas Gohr 2041c502704SAndreas Gohr /** 2051c502704SAndreas Gohr * @return int 2061c502704SAndreas Gohr */ 2071c502704SAndreas Gohr public function getId() { 2081c502704SAndreas Gohr return $this->id; 2091c502704SAndreas Gohr } 2101c502704SAndreas Gohr 2111c502704SAndreas Gohr /** 2121c502704SAndreas Gohr * @return \plugin\struct\meta\Column[] 2131c502704SAndreas Gohr */ 2141c502704SAndreas Gohr public function getColumns() { 2151c502704SAndreas Gohr return $this->columns; 2161c502704SAndreas Gohr } 2171c502704SAndreas Gohr 2181c502704SAndreas Gohr 2191c502704SAndreas Gohr 2201c502704SAndreas Gohr 221083afc55SAndreas Gohr} 222