146ca39b7SAndreas Gohr<?php 2d6d97f60SAnna Dabrowska 346ca39b7SAndreas Gohr/** 446ca39b7SAndreas Gohr * DokuWiki Plugin struct (Helper Component) 546ca39b7SAndreas Gohr * 646ca39b7SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 746ca39b7SAndreas Gohr * @author Andreas Gohr, Michael Große <dokuwiki@cosmocode.de> 846ca39b7SAndreas Gohr */ 946ca39b7SAndreas Gohr 107234bfb1Ssplitbrainuse dokuwiki\Extension\Plugin; 1193ca6f4fSAndreas Gohruse dokuwiki\plugin\struct\meta\AccessDataValidator; 12f411d872SAndreas Gohruse dokuwiki\plugin\struct\meta\AccessTable; 13ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\Assignments; 14ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\Schema; 15ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\StructException; 1646ca39b7SAndreas Gohr 1746ca39b7SAndreas Gohr/** 1846ca39b7SAndreas Gohr * The public interface for the struct plugin 1946ca39b7SAndreas Gohr * 207dac04ffSAndreas Gohr * 3rd party developers should always interact with struct data through this 2134ea6e10SAnna Dabrowska * helper plugin only. If additional interface functionality is needed, 227dac04ffSAndreas Gohr * it should be added here. 2346ca39b7SAndreas Gohr * 2446ca39b7SAndreas Gohr * All functions will throw StructExceptions when something goes wrong. 257dac04ffSAndreas Gohr * 267dac04ffSAndreas Gohr * Remember to check permissions yourself! 2746ca39b7SAndreas Gohr */ 287234bfb1Ssplitbrainclass helper_plugin_struct extends Plugin 29d6d97f60SAnna Dabrowska{ 3046ca39b7SAndreas Gohr /** 3134ea6e10SAnna Dabrowska * Class names of renderers which should NOT render struct data. 3234ea6e10SAnna Dabrowska * All descendants are also blacklisted. 3334ea6e10SAnna Dabrowska */ 3417a3a578SAndreas Gohr public const BLACKLIST_RENDERER = [ 3534ea6e10SAnna Dabrowska 'Doku_Renderer_metadata', 3634ea6e10SAnna Dabrowska '\renderer_plugin_qc' 3734ea6e10SAnna Dabrowska ]; 3834ea6e10SAnna Dabrowska 3934ea6e10SAnna Dabrowska /** 4046ca39b7SAndreas Gohr * Get the structured data of a given page 4146ca39b7SAndreas Gohr * 4246ca39b7SAndreas Gohr * @param string $page The page to get data for 4346ca39b7SAndreas Gohr * @param string|null $schema The schema to use null for all 44387ee210SAnna Dabrowska * @param int $time A timestamp if you want historic data 4546ca39b7SAndreas Gohr * @return array ('schema' => ( 'fieldlabel' => 'value', ...)) 4646ca39b7SAndreas Gohr * @throws StructException 4746ca39b7SAndreas Gohr */ 48d6d97f60SAnna Dabrowska public function getData($page, $schema = null, $time = 0) 49d6d97f60SAnna Dabrowska { 507dac04ffSAndreas Gohr $page = cleanID($page); 51387ee210SAnna Dabrowska if (!$time) { 52387ee210SAnna Dabrowska $time = time(); 53387ee210SAnna Dabrowska } 5446ca39b7SAndreas Gohr 557dac04ffSAndreas Gohr if (is_null($schema)) { 56025cb9daSAndreas Gohr $assignments = Assignments::getInstance(); 577dac04ffSAndreas Gohr $schemas = $assignments->getPageAssignments($page, false); 587dac04ffSAndreas Gohr } else { 597234bfb1Ssplitbrain $schemas = [$schema]; 607dac04ffSAndreas Gohr } 617dac04ffSAndreas Gohr 627234bfb1Ssplitbrain $result = []; 637dac04ffSAndreas Gohr foreach ($schemas as $schema) { 644cd5cc28SAnna Dabrowska $schemaData = AccessTable::getPageAccess($schema, $page, $time); 650dd23cefSAndreas Gohr $result[$schema] = $schemaData->getDataArray(); 667dac04ffSAndreas Gohr } 677dac04ffSAndreas Gohr 687dac04ffSAndreas Gohr return $result; 6946ca39b7SAndreas Gohr } 7046ca39b7SAndreas Gohr 7146ca39b7SAndreas Gohr /** 7246ca39b7SAndreas Gohr * Saves data for a given page (creates a new revision) 7346ca39b7SAndreas Gohr * 747dac04ffSAndreas Gohr * If this call succeeds you can assume your data has either been saved or it was 757dac04ffSAndreas Gohr * not necessary to save it because the data already existed in the wanted form or 767dac04ffSAndreas Gohr * the given schemas are no longer assigned to that page. 777dac04ffSAndreas Gohr * 787dac04ffSAndreas Gohr * Important: You have to check write permissions for the given page before calling 797dac04ffSAndreas Gohr * this function yourself! 807dac04ffSAndreas Gohr * 817dac04ffSAndreas Gohr * this duplicates a bit of code from entry.php - we could also fake post data and let 827dac04ffSAndreas Gohr * entry handle it, but that would be rather unclean and might be problematic when multiple 837dac04ffSAndreas Gohr * calls are done within the same request. 847dac04ffSAndreas Gohr * 8546ca39b7SAndreas Gohr * @param string $page 867dac04ffSAndreas Gohr * @param array $data ('schema' => ( 'fieldlabel' => 'value', ...)) 8746ca39b7SAndreas Gohr * @param string $summary 88178d3992SElan Ruusamäe * @param string $summary 8946ca39b7SAndreas Gohr * @throws StructException 900549dcc5SAndreas Gohr * @todo should this try to lock the page? 910549dcc5SAndreas Gohr * 920549dcc5SAndreas Gohr * 9346ca39b7SAndreas Gohr */ 94178d3992SElan Ruusamäe public function saveData($page, $data, $summary = '', $minor = false) 95178d3992SElan Ruusamäe { 967dac04ffSAndreas Gohr $page = cleanID($page); 977dac04ffSAndreas Gohr $summary = trim($summary); 987dac04ffSAndreas Gohr if (!$summary) $summary = $this->getLang('summary'); 9946ca39b7SAndreas Gohr 1007dac04ffSAndreas Gohr if (!page_exists($page)) throw new StructException("Page does not exist. You can not attach struct data"); 1017dac04ffSAndreas Gohr 1027dac04ffSAndreas Gohr // validate and see if anything changes 10393ca6f4fSAndreas Gohr $valid = AccessDataValidator::validateDataForPage($data, $page, $errors); 10493ca6f4fSAndreas Gohr if ($valid === false) { 1057234bfb1Ssplitbrain throw new StructException("Validation failed:\n%s", implode("\n", $errors)); 1067dac04ffSAndreas Gohr } 10793ca6f4fSAndreas Gohr if (!$valid) return; // empty array when no changes were detected 1087dac04ffSAndreas Gohr 109178d3992SElan Ruusamäe $newrevision = self::createPageRevision($page, $summary, $minor); 1107dac04ffSAndreas Gohr 1117dac04ffSAndreas Gohr // save the provided data 112025cb9daSAndreas Gohr $assignments = Assignments::getInstance(); 11393ca6f4fSAndreas Gohr foreach ($valid as $v) { 11493ca6f4fSAndreas Gohr $v->saveData($newrevision); 1157dac04ffSAndreas Gohr // make sure this schema is assigned 11693ca6f4fSAndreas Gohr $assignments->assignPageSchema($page, $v->getAccessTable()->getSchema()->getTable()); 1177dac04ffSAndreas Gohr } 11846ca39b7SAndreas Gohr } 11946ca39b7SAndreas Gohr 12046ca39b7SAndreas Gohr /** 12110575566SAnna Dabrowska * Save lookup data row 122ff2afc7cSMichael Große * 1230ceefd5cSAnna Dabrowska * @param AccessTable $access the table into which to save the data 124ff2afc7cSMichael Große * @param array $data data to be saved in the form of [columnName => 'data'] 125ff2afc7cSMichael Große */ 1260ceefd5cSAnna Dabrowska public function saveLookupData(AccessTable $access, $data) 127ff2afc7cSMichael Große { 128ff2afc7cSMichael Große if (!$access->getSchema()->isEditable()) { 129ff2afc7cSMichael Große throw new StructException('lookup save error: no permission for schema'); 130ff2afc7cSMichael Große } 131ff2afc7cSMichael Große $validator = $access->getValidator($data); 132ff2afc7cSMichael Große if (!$validator->validate()) { 133ff2afc7cSMichael Große throw new StructException("Validation failed:\n%s", implode("\n", $validator->getErrors())); 134ff2afc7cSMichael Große } 135ff2afc7cSMichael Große if (!$validator->saveData()) { 136ff2afc7cSMichael Große throw new StructException('No data saved'); 137ff2afc7cSMichael Große } 138ff2afc7cSMichael Große } 139ff2afc7cSMichael Große 140ff2afc7cSMichael Große /** 14113eddb0fSAndreas Gohr * Creates a new page revision with the same page content as before 14213eddb0fSAndreas Gohr * 14313eddb0fSAndreas Gohr * @param string $page 14413eddb0fSAndreas Gohr * @param string $summary 14513eddb0fSAndreas Gohr * @param bool $minor 14613eddb0fSAndreas Gohr * @return int the new revision 14713eddb0fSAndreas Gohr */ 148d6d97f60SAnna Dabrowska public static function createPageRevision($page, $summary = '', $minor = false) 149d6d97f60SAnna Dabrowska { 15013eddb0fSAndreas Gohr $summary = trim($summary); 15113eddb0fSAndreas Gohr // force a new page revision @see action_plugin_struct_entry::handle_pagesave_before() 15213eddb0fSAndreas Gohr $GLOBALS['struct_plugin_force_page_save'] = true; 15313eddb0fSAndreas Gohr saveWikiText($page, rawWiki($page), $summary, $minor); 15413eddb0fSAndreas Gohr unset($GLOBALS['struct_plugin_force_page_save']); 15513eddb0fSAndreas Gohr $file = wikiFN($page); 15613eddb0fSAndreas Gohr clearstatcache(false, $file); 15713eddb0fSAndreas Gohr return filemtime($file); 15813eddb0fSAndreas Gohr } 15913eddb0fSAndreas Gohr 16013eddb0fSAndreas Gohr /** 16146ca39b7SAndreas Gohr * Get info about existing schemas 16246ca39b7SAndreas Gohr * 16346ca39b7SAndreas Gohr * @param string|null $schema the schema to query, null for all 1647dac04ffSAndreas Gohr * @return Schema[] 16546ca39b7SAndreas Gohr * @throws StructException 16646ca39b7SAndreas Gohr */ 1676c9d1a10SAnna Dabrowska public static function getSchema($schema = null) 168d6d97f60SAnna Dabrowska { 1697dac04ffSAndreas Gohr if (is_null($schema)) { 1707dac04ffSAndreas Gohr $schemas = Schema::getAll(); 1717dac04ffSAndreas Gohr } else { 1727234bfb1Ssplitbrain $schemas = [$schema]; 1737dac04ffSAndreas Gohr } 17446ca39b7SAndreas Gohr 1757234bfb1Ssplitbrain $result = []; 1767dac04ffSAndreas Gohr foreach ($schemas as $table) { 1777dac04ffSAndreas Gohr $result[$table] = new Schema($table); 1787dac04ffSAndreas Gohr } 1797dac04ffSAndreas Gohr return $result; 18046ca39b7SAndreas Gohr } 18146ca39b7SAndreas Gohr 18246ca39b7SAndreas Gohr /** 18346ca39b7SAndreas Gohr * Returns all pages known to the struct plugin 18446ca39b7SAndreas Gohr * 18546ca39b7SAndreas Gohr * That means all pages that have or had once struct data saved 18646ca39b7SAndreas Gohr * 18746ca39b7SAndreas Gohr * @param string|null $schema limit the result to a given schema 1887dac04ffSAndreas Gohr * @return array (page => (schema => true), ...) 18946ca39b7SAndreas Gohr * @throws StructException 19046ca39b7SAndreas Gohr */ 191d6d97f60SAnna Dabrowska public function getPages($schema = null) 192d6d97f60SAnna Dabrowska { 193025cb9daSAndreas Gohr $assignments = Assignments::getInstance(); 1947dac04ffSAndreas Gohr return $assignments->getPages($schema); 19546ca39b7SAndreas Gohr } 196565b4cc1SAnna Dabrowska 19734ea6e10SAnna Dabrowska /** 19834ea6e10SAnna Dabrowska * Returns decoded JSON value or throws exception 19934ea6e10SAnna Dabrowska * when passed parameter is not JSON 20034ea6e10SAnna Dabrowska * 20134ea6e10SAnna Dabrowska * @param string $value 20234ea6e10SAnna Dabrowska * @return mixed 20334ea6e10SAnna Dabrowska * @throws StructException 204*4f8ee70dSAnna Dabrowska * @throws JsonException 20534ea6e10SAnna Dabrowska */ 206565b4cc1SAnna Dabrowska public static function decodeJson($value) 207565b4cc1SAnna Dabrowska { 208*4f8ee70dSAnna Dabrowska if (empty($value)) return $value; 209*4f8ee70dSAnna Dabrowska if ($value[0] !== '[') throw new StructException('Lookup expects JSON'); 2105e29103aSannda return json_decode($value, null, 512, JSON_THROW_ON_ERROR); 211565b4cc1SAnna Dabrowska } 21246ca39b7SAndreas Gohr} 213