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        $tablename = $INPUT->str('schema');
155
156        $schema = new Schema($tablename);
157        if (!$schema->isEditable()) {
158            return;
159        } // no permissions, no editor
160        // separate check for serial data in JS
161
162        echo '<div class="struct_entry_form">';
163        echo '<fieldset>';
164        echo '<legend>' . $this->getLang('lookup new entry') . '</legend>';
165        /** @var action_plugin_struct_edit $edit */
166        $edit = plugin_load('action', 'struct_edit');
167        foreach ($schema->getColumns(false) as $column) {
168            $label = $column->getLabel();
169            $field = new Value($column, '');
170            echo $edit->makeField($field, "entry[$label]");
171        }
172        formSecurityToken(); // csrf protection
173        echo '<input type="hidden" name="call" value="plugin_struct_aggregationeditor_save" />';
174        echo '<input type="hidden" name="schema" value="' . hsc($tablename) . '" />';
175
176        echo '<button type="submit">' . $lang['btn_save'] . '</button>';
177
178        echo '<div class="err"></div>';
179        echo '</fieldset>';
180        echo '</div>';
181    }
182
183    /**
184     * Returns data accessor
185     *
186     * @param string $tablename
187     * @return AccessTableGlobal
188     */
189    protected function getAccess($tablename)
190    {
191        if ($this->pid) {
192            return AccessTable::getSerialAccess($tablename, $this->pid, $this->rid);
193        }
194        return AccessTable::getGlobalAccess($tablename, $this->rid);
195    }
196
197    /**
198     * Adds filter to search config to differentiate data types
199     *
200     * @param array $config
201     * @return array
202     */
203    protected function addTypeFilter($config)
204    {
205        $config['filter'][] = ['%rowid%', '=', $this->rid, 'AND'];
206        if ($this->pid) {
207            $config['filter'][] = ['%pageid%', '=', $this->pid, 'AND'];
208        }
209        return $config;
210    }
211
212    /**
213     * Throws an exception if data is invalid
214     */
215    protected function validate()
216    {
217        if (!$this->rid) {
218            throw new StructException('No row id given');
219        }
220    }
221}
222