1308cc83fSAndreas Gohr<?php 2308cc83fSAndreas Gohr 3308cc83fSAndreas Gohr/** 4308cc83fSAndreas Gohr * DokuWiki Plugin struct (Action Component) 5308cc83fSAndreas Gohr * 6308cc83fSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 7308cc83fSAndreas Gohr * @author Andreas Gohr, Michael Große <dokuwiki@cosmocode.de> 8308cc83fSAndreas Gohr */ 9308cc83fSAndreas Gohr 107234bfb1Ssplitbrainuse dokuwiki\Extension\ActionPlugin; 117234bfb1Ssplitbrainuse dokuwiki\Extension\EventHandler; 127234bfb1Ssplitbrainuse dokuwiki\Extension\Event; 13308cc83fSAndreas Gohruse dokuwiki\plugin\struct\meta\AccessTable; 14308cc83fSAndreas Gohruse dokuwiki\plugin\struct\meta\AccessTableGlobal; 15308cc83fSAndreas Gohruse dokuwiki\plugin\struct\meta\AggregationEditorTable; 160549dcc5SAndreas Gohruse dokuwiki\plugin\struct\meta\Column; 17308cc83fSAndreas Gohruse dokuwiki\plugin\struct\meta\Schema; 18308cc83fSAndreas Gohruse dokuwiki\plugin\struct\meta\SearchConfig; 19308cc83fSAndreas Gohruse dokuwiki\plugin\struct\meta\StructException; 20308cc83fSAndreas Gohruse dokuwiki\plugin\struct\meta\Value; 21308cc83fSAndreas Gohr 22308cc83fSAndreas Gohr/** 23308cc83fSAndreas Gohr * Class action_plugin_struct_lookup 24308cc83fSAndreas Gohr * 25308cc83fSAndreas Gohr * Handle global and serial data table editing 26308cc83fSAndreas Gohr */ 277234bfb1Ssplitbrainclass action_plugin_struct_aggregationeditor extends ActionPlugin 28308cc83fSAndreas Gohr{ 29308cc83fSAndreas Gohr /** @var Column */ 307234bfb1Ssplitbrain protected $column; 31308cc83fSAndreas Gohr 32308cc83fSAndreas Gohr /** @var string */ 33308cc83fSAndreas Gohr protected $pid = ''; 34308cc83fSAndreas Gohr 35308cc83fSAndreas Gohr /** @var int */ 36308cc83fSAndreas Gohr protected $rid = 0; 37308cc83fSAndreas Gohr 38308cc83fSAndreas Gohr /** 39308cc83fSAndreas Gohr * Registers a callback function for a given event 40308cc83fSAndreas Gohr * 41*5e29103aSannda * @param EventHandler $controller DokuWiki's event controller object 42308cc83fSAndreas Gohr * @return void 43308cc83fSAndreas Gohr */ 447234bfb1Ssplitbrain public function register(EventHandler $controller) 45308cc83fSAndreas Gohr { 4617dda596SAnna Dabrowska $controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'addJsinfo'); 47308cc83fSAndreas Gohr $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjax'); 48308cc83fSAndreas Gohr } 49308cc83fSAndreas Gohr 50308cc83fSAndreas Gohr /** 5117dda596SAnna Dabrowska * Add user's permissions to JSINFO 5217dda596SAnna Dabrowska * 53*5e29103aSannda * @param Event $event 54308cc83fSAndreas Gohr */ 557234bfb1Ssplitbrain public function addJsinfo(Event $event) 5617dda596SAnna Dabrowska { 5717dda596SAnna Dabrowska global $ID; 5817dda596SAnna Dabrowska global $JSINFO; 597234bfb1Ssplitbrain $JSINFO['plugins']['struct']['isPageEditor'] = auth_quickaclcheck($ID) >= AUTH_EDIT; 6017dda596SAnna Dabrowska } 6117dda596SAnna Dabrowska 6217dda596SAnna Dabrowska 6317dda596SAnna Dabrowska /** 64*5e29103aSannda * @param Event $event 6517dda596SAnna Dabrowska */ 667234bfb1Ssplitbrain public function handleAjax(Event $event) 67308cc83fSAndreas Gohr { 68308cc83fSAndreas Gohr $len = strlen('plugin_struct_aggregationeditor_'); 69308cc83fSAndreas Gohr if (substr($event->data, 0, $len) != 'plugin_struct_aggregationeditor_') { 70308cc83fSAndreas Gohr return; 71308cc83fSAndreas Gohr } 72308cc83fSAndreas Gohr $event->preventDefault(); 73308cc83fSAndreas Gohr $event->stopPropagation(); 74308cc83fSAndreas Gohr 75308cc83fSAndreas Gohr try { 76308cc83fSAndreas Gohr if (substr($event->data, $len) == 'new') { 77308cc83fSAndreas Gohr $this->newRowEditor(); 78308cc83fSAndreas Gohr } 79308cc83fSAndreas Gohr 80308cc83fSAndreas Gohr if (substr($event->data, $len) == 'save') { 81308cc83fSAndreas Gohr $this->saveRow(); 82308cc83fSAndreas Gohr } 83308cc83fSAndreas Gohr 84308cc83fSAndreas Gohr if (substr($event->data, $len) == 'delete') { 85308cc83fSAndreas Gohr $this->deleteRow(); 86308cc83fSAndreas Gohr } 87308cc83fSAndreas Gohr } catch (StructException $e) { 88308cc83fSAndreas Gohr http_status(500); 89308cc83fSAndreas Gohr header('Content-Type: text/plain'); 90308cc83fSAndreas Gohr echo $e->getMessage(); 91308cc83fSAndreas Gohr } 92308cc83fSAndreas Gohr } 93308cc83fSAndreas Gohr 94308cc83fSAndreas Gohr /** 95308cc83fSAndreas Gohr * Deletes a row 96308cc83fSAndreas Gohr */ 97308cc83fSAndreas Gohr protected function deleteRow() 98308cc83fSAndreas Gohr { 99308cc83fSAndreas Gohr global $INPUT; 100308cc83fSAndreas Gohr $tablename = $INPUT->str('schema'); 101308cc83fSAndreas Gohr if (!$tablename) { 102308cc83fSAndreas Gohr throw new StructException('No schema given'); 103308cc83fSAndreas Gohr } 104308cc83fSAndreas Gohr 105308cc83fSAndreas Gohr $this->rid = $INPUT->int('rid'); 106308cc83fSAndreas Gohr $this->validate(); 107308cc83fSAndreas Gohr 108308cc83fSAndreas Gohr action_plugin_struct_inline::checkCSRF(); 109308cc83fSAndreas Gohr 110308cc83fSAndreas Gohr $access = $this->getAccess($tablename); 111308cc83fSAndreas Gohr if (!$access->getSchema()->isEditable()) { 112308cc83fSAndreas Gohr throw new StructException('lookup delete error: no permission for schema'); 113308cc83fSAndreas Gohr } 114308cc83fSAndreas Gohr $access->clearData(); 115308cc83fSAndreas Gohr } 116308cc83fSAndreas Gohr 117308cc83fSAndreas Gohr /** 118308cc83fSAndreas Gohr * Save one new row 119308cc83fSAndreas Gohr */ 120308cc83fSAndreas Gohr protected function saveRow() 121308cc83fSAndreas Gohr { 122308cc83fSAndreas Gohr global $INPUT; 123308cc83fSAndreas Gohr $tablename = $INPUT->str('schema'); 124308cc83fSAndreas Gohr $data = $INPUT->arr('entry'); 125308cc83fSAndreas Gohr $this->pid = $INPUT->str('pid'); 126308cc83fSAndreas Gohr action_plugin_struct_inline::checkCSRF(); 127308cc83fSAndreas Gohr 128308cc83fSAndreas Gohr // create a new row based on the original aggregation config 129308cc83fSAndreas Gohr $access = $this->getAccess($tablename); 130308cc83fSAndreas Gohr 131308cc83fSAndreas Gohr /** @var helper_plugin_struct $helper */ 132308cc83fSAndreas Gohr $helper = plugin_load('helper', 'struct'); 133308cc83fSAndreas Gohr $helper->saveLookupData($access, $data); 134308cc83fSAndreas Gohr 135*5e29103aSannda $config = json_decode($INPUT->str('searchconf'), true, 512, JSON_THROW_ON_ERROR); 136308cc83fSAndreas Gohr // update row id 137308cc83fSAndreas Gohr $this->rid = $access->getRid(); 138308cc83fSAndreas Gohr $config = $this->addTypeFilter($config); 139308cc83fSAndreas Gohr 140308cc83fSAndreas Gohr $editorTable = new AggregationEditorTable( 141308cc83fSAndreas Gohr $this->pid, 142308cc83fSAndreas Gohr 'xhtml', 143308cc83fSAndreas Gohr new Doku_Renderer_xhtml(), 144308cc83fSAndreas Gohr new SearchConfig($config) 145308cc83fSAndreas Gohr ); 146308cc83fSAndreas Gohr 147308cc83fSAndreas Gohr echo $editorTable->getFirstRow(); 148308cc83fSAndreas Gohr } 149308cc83fSAndreas Gohr 150308cc83fSAndreas Gohr /** 151308cc83fSAndreas Gohr * Create the Editor for a new row 152308cc83fSAndreas Gohr */ 153308cc83fSAndreas Gohr protected function newRowEditor() 154308cc83fSAndreas Gohr { 155308cc83fSAndreas Gohr global $INPUT; 156308cc83fSAndreas Gohr global $lang; 157615e5131SAnna Dabrowska 158307755deSAnna Dabrowska $searchconf = $INPUT->arr('searchconf'); 159307755deSAnna Dabrowska $tablename = $searchconf['schemas'][0][0]; 160308cc83fSAndreas Gohr 161308cc83fSAndreas Gohr $schema = new Schema($tablename); 162308cc83fSAndreas Gohr if (!$schema->isEditable()) { 163308cc83fSAndreas Gohr return; 164308cc83fSAndreas Gohr } // no permissions, no editor 16517dda596SAnna Dabrowska // separate check for serial data in JS 166308cc83fSAndreas Gohr 167308cc83fSAndreas Gohr echo '<div class="struct_entry_form">'; 168308cc83fSAndreas Gohr echo '<fieldset>'; 169308cc83fSAndreas Gohr echo '<legend>' . $this->getLang('lookup new entry') . '</legend>'; 170308cc83fSAndreas Gohr /** @var action_plugin_struct_edit $edit */ 171308cc83fSAndreas Gohr $edit = plugin_load('action', 'struct_edit'); 17287788c7fSAnna Dabrowska 17387788c7fSAnna Dabrowska // filter columns based on searchconf cols from syntax 174307755deSAnna Dabrowska $columns = $this->resolveColumns($searchconf, $schema); 17587788c7fSAnna Dabrowska 17687788c7fSAnna Dabrowska foreach ($columns as $column) { 177308cc83fSAndreas Gohr $label = $column->getLabel(); 178308cc83fSAndreas Gohr $field = new Value($column, ''); 179308cc83fSAndreas Gohr echo $edit->makeField($field, "entry[$label]"); 180308cc83fSAndreas Gohr } 181308cc83fSAndreas Gohr formSecurityToken(); // csrf protection 182308cc83fSAndreas Gohr echo '<input type="hidden" name="call" value="plugin_struct_aggregationeditor_save" />'; 183308cc83fSAndreas Gohr echo '<input type="hidden" name="schema" value="' . hsc($tablename) . '" />'; 184308cc83fSAndreas Gohr 185308cc83fSAndreas Gohr echo '<button type="submit">' . $lang['btn_save'] . '</button>'; 186308cc83fSAndreas Gohr 187308cc83fSAndreas Gohr echo '<div class="err"></div>'; 188308cc83fSAndreas Gohr echo '</fieldset>'; 189308cc83fSAndreas Gohr echo '</div>'; 190308cc83fSAndreas Gohr } 191308cc83fSAndreas Gohr 192308cc83fSAndreas Gohr /** 193615e5131SAnna Dabrowska * Names of columns in the new entry editor: either all, 194615e5131SAnna Dabrowska * or the selection defined in config. If config contains '*', 195615e5131SAnna Dabrowska * just return the full set. 196615e5131SAnna Dabrowska * 197615e5131SAnna Dabrowska * @param array $searchconf 198615e5131SAnna Dabrowska * @param Schema $schema 199615e5131SAnna Dabrowska * @return array 200615e5131SAnna Dabrowska */ 201615e5131SAnna Dabrowska protected function resolveColumns($searchconf, $schema) 202615e5131SAnna Dabrowska { 203615e5131SAnna Dabrowska // if no valid column config, return all columns 204615e5131SAnna Dabrowska if ( 205615e5131SAnna Dabrowska empty($searchconf['cols']) || 206615e5131SAnna Dabrowska !is_array($searchconf['cols']) || 207615e5131SAnna Dabrowska in_array('*', $searchconf['cols']) 208615e5131SAnna Dabrowska ) { 209615e5131SAnna Dabrowska return $schema->getColumns(false); 210615e5131SAnna Dabrowska } 211615e5131SAnna Dabrowska 212615e5131SAnna Dabrowska $columns = []; 213615e5131SAnna Dabrowska foreach ($searchconf['cols'] as $col) { 214615e5131SAnna Dabrowska $columns[] = $schema->findColumn($col); 215615e5131SAnna Dabrowska } 216615e5131SAnna Dabrowska // filter invalid columns (where findColumn() returned false) 217615e5131SAnna Dabrowska return array_filter($columns); 218615e5131SAnna Dabrowska } 219615e5131SAnna Dabrowska 220615e5131SAnna Dabrowska /** 221308cc83fSAndreas Gohr * Returns data accessor 222308cc83fSAndreas Gohr * 223308cc83fSAndreas Gohr * @param string $tablename 224308cc83fSAndreas Gohr * @return AccessTableGlobal 225308cc83fSAndreas Gohr */ 226308cc83fSAndreas Gohr protected function getAccess($tablename) 227308cc83fSAndreas Gohr { 228308cc83fSAndreas Gohr if ($this->pid) { 229308cc83fSAndreas Gohr return AccessTable::getSerialAccess($tablename, $this->pid, $this->rid); 230308cc83fSAndreas Gohr } 231ed77599cSAnna Dabrowska return AccessTable::getGlobalAccess($tablename, $this->rid); 232308cc83fSAndreas Gohr } 233308cc83fSAndreas Gohr 234308cc83fSAndreas Gohr /** 235308cc83fSAndreas Gohr * Adds filter to search config to differentiate data types 236308cc83fSAndreas Gohr * 237308cc83fSAndreas Gohr * @param array $config 238308cc83fSAndreas Gohr * @return array 239308cc83fSAndreas Gohr */ 240308cc83fSAndreas Gohr protected function addTypeFilter($config) 241308cc83fSAndreas Gohr { 242308cc83fSAndreas Gohr $config['filter'][] = ['%rowid%', '=', $this->rid, 'AND']; 243308cc83fSAndreas Gohr if ($this->pid) { 244308cc83fSAndreas Gohr $config['filter'][] = ['%pageid%', '=', $this->pid, 'AND']; 245308cc83fSAndreas Gohr } 246308cc83fSAndreas Gohr return $config; 247308cc83fSAndreas Gohr } 248308cc83fSAndreas Gohr 249308cc83fSAndreas Gohr /** 250308cc83fSAndreas Gohr * Throws an exception if data is invalid 251308cc83fSAndreas Gohr */ 252308cc83fSAndreas Gohr protected function validate() 253308cc83fSAndreas Gohr { 254308cc83fSAndreas Gohr if (!$this->rid) { 255308cc83fSAndreas Gohr throw new StructException('No row id given'); 256308cc83fSAndreas Gohr } 257308cc83fSAndreas Gohr } 258308cc83fSAndreas Gohr} 259