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\Extension\ActionPlugin;
11use dokuwiki\Extension\EventHandler;
12use dokuwiki\Extension\Event;
13use dokuwiki\plugin\struct\meta\AccessTable;
14use dokuwiki\plugin\struct\meta\Assignments;
15use dokuwiki\plugin\struct\meta\Schema;
16use dokuwiki\plugin\struct\meta\Search;
17use dokuwiki\plugin\struct\types\Lookup;
18
19/**
20 * Handles bureaucracy additions
21 *
22 * This registers to the template action of the bureaucracy plugin and saves all struct data
23 * submitted through the bureaucracy form to all newly created pages (if the schema applies).
24 *
25 * It also registers the struct_schema type for bureaucracy which will add all fields of the
26 * schema to the form. The struct_field type is added through standard naming convention - see
27 * helper/fiels.php for that.
28 */
29class action_plugin_struct_bureaucracy extends ActionPlugin
30{
31    /**
32     * Registers a callback function for a given event
33     *
34     * @param EventHandler $controller DokuWiki's event controller object
35     * @return void
36     */
37    public function register(EventHandler $controller)
38    {
39        $controller->register_hook('PLUGIN_BUREAUCRACY_PAGENAME', 'BEFORE', $this, 'handleLookupFields');
40        $controller->register_hook('PLUGIN_BUREAUCRACY_EMAIL_SEND', 'BEFORE', $this, 'handleLookupFields');
41        $controller->register_hook('PLUGIN_BUREAUCRACY_TEMPLATE_SAVE', 'AFTER', $this, 'handleSave');
42        $controller->register_hook('PLUGIN_BUREAUCRACY_FIELD_UNKNOWN', 'BEFORE', $this, 'handleSchema');
43    }
44
45    /**
46     * Load a whole schema as fields
47     *
48     * @param Event $event event object by reference
49     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
50     *                           handler was registered]
51     * @return bool
52     */
53    public function handleSchema(Event $event, $param)
54    {
55        $args = $event->data['args'];
56        if ($args[0] != 'struct_schema') return false;
57        $event->preventDefault();
58        $event->stopPropagation();
59
60        /** @var helper_plugin_bureaucracy_field $helper */
61        $helper = plugin_load('helper', 'bureaucracy_field');
62        $helper->initialize($args);
63
64        $schema = new Schema($helper->opt['label']);
65        if (!$schema->getId()) {
66            msg('This schema does not exist', -1);
67            return false;
68        }
69
70        foreach ($schema->getColumns(false) as $column) {
71            /** @var helper_plugin_struct_field $field */
72            $field = plugin_load('helper', 'struct_field');
73            // we don't initialize the field but set the appropriate values
74            $field->opt = $helper->opt; // copy all the settings to each field
75            $field->opt['label'] = $column->getFullQualifiedLabel();
76            $field->column = $column;
77            $event->data['fields'][] = $field;
78        }
79        return true;
80    }
81
82    /**
83     * Replace lookup fields placeholder's values
84     *
85     * @param Event $event event object by reference
86     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
87     *                           handler was registered]
88     * @return bool
89     */
90    public function handleLookupFields(Event $event, $param)
91    {
92        foreach ($event->data['fields'] as $field) {
93            if (!is_a($field, 'helper_plugin_struct_field')) continue;
94            if (!$field->column->getType() instanceof Lookup) continue;
95
96            $value = $field->getParam('value');
97            if (!is_array($value)) $value = [$value];
98
99            $config = $field->column->getType()->getConfig();
100
101            // find proper value
102            // current Search implementation doesn't allow doing it using SQL
103            $search = new Search();
104            $search->addSchema($config['schema']);
105            $search->addColumn($config['field']);
106            $result = $search->getRows();
107            $pids = $search->getPids();
108            $rids = $search->getRids();
109
110            $field->opt['struct_pids'] = [];
111            $new_value = [];
112            foreach ($value as $pid) {
113                $counter = count($result);
114                for ($i = 0; $i < $counter; $i++) {
115                    // lookups can reference pages or global data, so check both pid and rid
116                    // make sure not to double decode pid!
117                    $originalPid = $pid;
118                    // do not throw JSON exception here, we supply alternative values if json_decode doesn't
119                    $pid = json_decode($pid, null, 512)[0] ?? $pid;
120                    $rid = json_decode($originalPid, null, 512)[1] ?? null;
121                    if (($pid && $pids[$i] === $pid) || ($rid && $rids[$i] === $rid)) {
122                        $field->opt['struct_pids'][] = $pid;
123                        $new_value[] = $result[$i][0]->getDisplayValue();
124                    }
125                }
126            }
127
128            //replace previous value
129            if ($field->column->isMulti()) {
130                $field->opt['value'] = $new_value;
131            } else {
132                $event->data['values'][$field->column->getFullQualifiedLabel()] = $new_value[0] ?? '';
133            }
134        }
135        return true;
136    }
137
138    /**
139     * Save the struct data
140     *
141     * @param Event $event event object by reference
142     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
143     *                           handler was registered]
144     * @return bool
145     */
146    public function handleSave(Event $event, $param)
147    {
148        // get all struct values and their associated schemas
149        $tosave = [];
150        foreach ($event->data['fields'] as $field) {
151            if (!is_a($field, 'helper_plugin_struct_field')) continue;
152            /** @var helper_plugin_struct_field $field */
153            $tbl = $field->column->getTable();
154            $lbl = $field->column->getLabel();
155            if (!isset($tosave[$tbl])) $tosave[$tbl] = [];
156
157            if ($field->column->isMulti() && $field->column->getType() instanceof Lookup) {
158                $tosave[$tbl][$lbl] = $field->opt['struct_pids'];
159            } else {
160                $tosave[$tbl][$lbl] = $field->getParam('value');
161            }
162        }
163
164        // save all the struct data of assigned schemas
165        $id = $event->data['id'];
166        $time = filemtime(wikiFN($id));
167
168        $assignments = Assignments::getInstance();
169        $assigned = $assignments->getPageAssignments($id);
170        foreach ($tosave as $table => $data) {
171            if (!in_array($table, $assigned)) continue;
172            $access = AccessTable::getPageAccess($table, $id, $time);
173            $validator = $access->getValidator($data);
174            if ($validator->validate()) {
175                $validator->saveData($time);
176
177                // make sure this schema is assigned
178                $assignments->assignPageSchema(
179                    $id,
180                    $validator->getAccessTable()->getSchema()->getTable()
181                );
182
183                // trigger meta data rendering to set page title
184                p_get_metadata($id);
185            }
186        }
187
188        return true;
189    }
190}
191
192// vim:ts=4:sw=4:et:
193