xref: /plugin/struct/helper.php (revision 4f8ee70d57ce4e854bee21a770c761c27b14ff90)
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