xref: /plugin/struct/action/aggregationeditor.php (revision 67f70d54d911c6c842e298d4a97622e9d01815b3)
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