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\AccessDataValidator;
14use dokuwiki\plugin\struct\meta\AccessTable;
15use dokuwiki\plugin\struct\meta\Assignments;
16
17/**
18 * Class action_plugin_struct_entry
19 *
20 * Handles the entry process of struct data with type "page"
21 */
22class action_plugin_struct_entry extends ActionPlugin
23{
24    /**
25     * @var string The form name we use to transfer schema data
26     */
27    protected static $VAR = 'struct_schema_data';
28
29    /** @var helper_plugin_sqlite */
30    protected $sqlite;
31
32    /** @var  bool has the data been validated correctly? */
33    protected $validated;
34
35    /** @var  AccessDataValidator[] these schemas are validated and have changed data and need to be saved */
36    protected $tosave;
37
38    /**
39     * Registers a callback function for a given event
40     *
41     * @param EventHandler $controller DokuWiki's event controller object
42     * @return void
43     */
44    public function register(EventHandler $controller)
45    {
46        // validate data on preview and save;
47        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleValidation');
48        // ensure a page revision is created when struct data changes:
49        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'BEFORE', $this, 'handlePagesaveBefore');
50        // save struct data after page has been saved:
51        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'AFTER', $this, 'handlePagesaveAfter');
52    }
53
54    /**
55     * Clean up and validate the input data
56     *
57     * @param Event $event event object by reference
58     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
59     *                           handler was registered]
60     * @return bool
61     */
62    public function handleValidation(Event $event, $param)
63    {
64        global $ID, $INPUT;
65        $act = act_clean($event->data);
66        if (!in_array($act, ['save', 'preview'])) return false;
67        $this->tosave = [];
68
69        // run the validation for each assignded schema
70        $valid = AccessDataValidator::validateDataForPage($INPUT->arr(self::$VAR), $ID, $errors);
71        if ($valid === false) {
72            $this->validated = false;
73            foreach ($errors as $error) {
74                msg(hsc($error), -1);
75            }
76        } else {
77            $this->validated = true;
78            $this->tosave = $valid;
79        }
80
81        // FIXME we used to set the cleaned data as new input data. this caused #140
82        // could we just not do that, and keep the cleaning to saving only? and fix that bug this way?
83
84        // did validation go through? otherwise abort saving
85        if (!$this->validated && $act == 'save') {
86            $event->data = 'edit';
87        }
88
89        return true;
90    }
91
92    /**
93     * Check if the page has to be changed
94     *
95     * @param Event $event event object by reference
96     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
97     *                           handler was registered]
98     * @return bool
99     */
100    public function handlePagesaveBefore(Event $event, $param)
101    {
102        if ($event->data['contentChanged']) return false; // will be saved for page changes
103        global $ACT;
104        if ($ACT == 'revert') return false; // this is handled in revert.php
105
106        if ((is_array($this->tosave) && count($this->tosave)) || isset($GLOBALS['struct_plugin_force_page_save'])) {
107            if (trim($event->data['newContent']) === '') {
108                // this happens when a new page is tried to be created with only struct data
109                msg($this->getLang('emptypage'), -1);
110            } else {
111                $event->data['contentChanged'] = true; // save for data changes
112
113                // add a summary
114                if (empty($event->data['summary'])) {
115                    $event->data['summary'] = $this->getLang('summary');
116                }
117            }
118        }
119
120        return true;
121    }
122
123    /**
124     * Save the data
125     *
126     * When this is called, INPUT data has been validated already.
127     *
128     * @param Event $event event object by reference
129     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
130     *                           handler was registered]
131     * @return bool
132     */
133    public function handlePagesaveAfter(Event $event, $param)
134    {
135        global $ACT;
136        if ($ACT == 'revert') return false; // handled in revert
137
138        $assignments = Assignments::getInstance();
139        if ($event->data['changeType'] == DOKU_CHANGE_TYPE_DELETE && empty($GLOBALS['PLUGIN_MOVE_WORKING'])) {
140            // clear all data on delete unless it's a move operation
141            $tables = $assignments->getPageAssignments($event->data['id']);
142            foreach ($tables as $table) {
143                $schemaData = AccessTable::getPageAccess($table, $event->data['id']);
144                if ($schemaData->getSchema()->isEditable()) {
145                    $schemaData->clearData();
146                }
147            }
148        } elseif ($this->tosave) {
149            // save the provided data
150            foreach ($this->tosave as $validation) {
151                if ($validation->getAccessTable()->getSchema()->isEditable()) {
152                    $validation->saveData($event->data['newRevision']);
153
154                    // make sure this schema is assigned
155                    $assignments->assignPageSchema(
156                        $event->data['id'],
157                        $validation->getAccessTable()->getSchema()->getTable()
158                    );
159                }
160            }
161        }
162        return true;
163    }
164}
165
166// vim:ts=4:sw=4:et:
167