1<?php 2 3/** 4 * DokuWiki Plugin struct (Action Component) 5 * 6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 7 * @author Andreas Gohr, Michael Große <dokuwiki@cosmocode.de> 8 */ 9 10use dokuwiki\plugin\struct\meta\AccessTable; 11use dokuwiki\plugin\struct\meta\AccessTableGlobal; 12use dokuwiki\plugin\struct\meta\AggregationEditorTable; 13use dokuwiki\plugin\struct\meta\Column; 14use dokuwiki\plugin\struct\meta\Schema; 15use dokuwiki\plugin\struct\meta\SearchConfig; 16use dokuwiki\plugin\struct\meta\StructException; 17use dokuwiki\plugin\struct\meta\Value; 18 19/** 20 * Class action_plugin_struct_lookup 21 * 22 * Handle global and serial data table editing 23 */ 24class action_plugin_struct_aggregationeditor extends DokuWiki_Action_Plugin 25{ 26 /** @var Column */ 27 protected $column = null; 28 29 /** @var string */ 30 protected $pid = ''; 31 32 /** @var int */ 33 protected $rid = 0; 34 35 /** 36 * Registers a callback function for a given event 37 * 38 * @param Doku_Event_Handler $controller DokuWiki's event controller object 39 * @return void 40 */ 41 public function register(Doku_Event_Handler $controller) 42 { 43 $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'addJsinfo'); 44 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjax'); 45 } 46 47 /** 48 * Add user's permissions to JSINFO 49 * 50 * @param Doku_Event $event 51 */ 52 public function addJsinfo(Doku_Event $event) 53 { 54 global $ID; 55 global $JSINFO; 56 $JSINFO['plugins']['struct']['isPageEditor'] = (bool)(auth_quickaclcheck($ID) >= AUTH_EDIT); 57 } 58 59 60 /** 61 * @param Doku_Event $event 62 */ 63 public function handleAjax(Doku_Event $event) 64 { 65 $len = strlen('plugin_struct_aggregationeditor_'); 66 if (substr($event->data, 0, $len) != 'plugin_struct_aggregationeditor_') { 67 return; 68 } 69 $event->preventDefault(); 70 $event->stopPropagation(); 71 72 try { 73 if (substr($event->data, $len) == 'new') { 74 $this->newRowEditor(); 75 } 76 77 if (substr($event->data, $len) == 'save') { 78 $this->saveRow(); 79 } 80 81 if (substr($event->data, $len) == 'delete') { 82 $this->deleteRow(); 83 } 84 } catch (StructException $e) { 85 http_status(500); 86 header('Content-Type: text/plain'); 87 echo $e->getMessage(); 88 } 89 } 90 91 /** 92 * Deletes a row 93 */ 94 protected function deleteRow() 95 { 96 global $INPUT; 97 $tablename = $INPUT->str('schema'); 98 if (!$tablename) { 99 throw new StructException('No schema given'); 100 } 101 102 $this->rid = $INPUT->int('rid'); 103 $this->validate(); 104 105 action_plugin_struct_inline::checkCSRF(); 106 107 $access = $this->getAccess($tablename); 108 if (!$access->getSchema()->isEditable()) { 109 throw new StructException('lookup delete error: no permission for schema'); 110 } 111 $access->clearData(); 112 } 113 114 /** 115 * Save one new row 116 */ 117 protected function saveRow() 118 { 119 global $INPUT; 120 $tablename = $INPUT->str('schema'); 121 $data = $INPUT->arr('entry'); 122 $this->pid = $INPUT->str('pid'); 123 action_plugin_struct_inline::checkCSRF(); 124 125 // create a new row based on the original aggregation config 126 $access = $this->getAccess($tablename); 127 128 /** @var helper_plugin_struct $helper */ 129 $helper = plugin_load('helper', 'struct'); 130 $helper->saveLookupData($access, $data); 131 132 $config = json_decode($INPUT->str('searchconf'), true); 133 // update row id 134 $this->rid = $access->getRid(); 135 $config = $this->addTypeFilter($config); 136 137 $editorTable = new AggregationEditorTable( 138 $this->pid, 139 'xhtml', 140 new Doku_Renderer_xhtml(), 141 new SearchConfig($config) 142 ); 143 144 echo $editorTable->getFirstRow(); 145 } 146 147 /** 148 * Create the Editor for a new row 149 */ 150 protected function newRowEditor() 151 { 152 global $INPUT; 153 global $lang; 154 155 $searchconf = $INPUT->arr('searchconf'); 156 $tablename = $searchconf['schemas'][0][0]; 157 158 $schema = new Schema($tablename); 159 if (!$schema->isEditable()) { 160 return; 161 } // no permissions, no editor 162 // separate check for serial data in JS 163 164 echo '<div class="struct_entry_form">'; 165 echo '<fieldset>'; 166 echo '<legend>' . $this->getLang('lookup new entry') . '</legend>'; 167 /** @var action_plugin_struct_edit $edit */ 168 $edit = plugin_load('action', 'struct_edit'); 169 170 // filter columns based on searchconf cols from syntax 171 $columns = $this->resolveColumns($searchconf, $schema); 172 173 foreach ($columns as $column) { 174 $label = $column->getLabel(); 175 $field = new Value($column, ''); 176 echo $edit->makeField($field, "entry[$label]"); 177 } 178 formSecurityToken(); // csrf protection 179 echo '<input type="hidden" name="call" value="plugin_struct_aggregationeditor_save" />'; 180 echo '<input type="hidden" name="schema" value="' . hsc($tablename) . '" />'; 181 182 echo '<button type="submit">' . $lang['btn_save'] . '</button>'; 183 184 echo '<div class="err"></div>'; 185 echo '</fieldset>'; 186 echo '</div>'; 187 } 188 189 /** 190 * Names of columns in the new entry editor: either all, 191 * or the selection defined in config. If config contains '*', 192 * just return the full set. 193 * 194 * @param array $searchconf 195 * @param Schema $schema 196 * @return array 197 */ 198 protected function resolveColumns($searchconf, $schema) 199 { 200 // if no valid column config, return all columns 201 if ( 202 empty($searchconf['cols']) || 203 !is_array($searchconf['cols']) || 204 in_array('*', $searchconf['cols']) 205 ) { 206 return $schema->getColumns(false); 207 } 208 209 $columns = []; 210 foreach ($searchconf['cols'] as $col) { 211 $columns[] = $schema->findColumn($col); 212 } 213 // filter invalid columns (where findColumn() returned false) 214 return array_filter($columns); 215 } 216 217 /** 218 * Returns data accessor 219 * 220 * @param string $tablename 221 * @return AccessTableGlobal 222 */ 223 protected function getAccess($tablename) 224 { 225 if ($this->pid) { 226 return AccessTable::getSerialAccess($tablename, $this->pid, $this->rid); 227 } 228 return AccessTable::getGlobalAccess($tablename, $this->rid); 229 } 230 231 /** 232 * Adds filter to search config to differentiate data types 233 * 234 * @param array $config 235 * @return array 236 */ 237 protected function addTypeFilter($config) 238 { 239 $config['filter'][] = ['%rowid%', '=', $this->rid, 'AND']; 240 if ($this->pid) { 241 $config['filter'][] = ['%pageid%', '=', $this->pid, 'AND']; 242 } 243 return $config; 244 } 245 246 /** 247 * Throws an exception if data is invalid 248 */ 249 protected function validate() 250 { 251 if (!$this->rid) { 252 throw new StructException('No row id given'); 253 } 254 } 255} 256