xref: /plugin/struct/action/entry.php (revision f411d87222d57469b74e2810a1ff3b1f60d1daa8)
1549a0837SAndreas Gohr<?php
2549a0837SAndreas Gohr/**
3549a0837SAndreas Gohr * DokuWiki Plugin struct (Action Component)
4549a0837SAndreas Gohr *
5549a0837SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6549a0837SAndreas Gohr * @author  Andreas Gohr, Michael Große <dokuwiki@cosmocode.de>
7549a0837SAndreas Gohr */
8549a0837SAndreas Gohr
9549a0837SAndreas Gohr// must be run within Dokuwiki
10549a0837SAndreas Gohrif(!defined('DOKU_INC')) die();
11549a0837SAndreas Gohr
12*f411d872SAndreas Gohruse dokuwiki\plugin\struct\meta\AccessTable;
13ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\Assignments;
14ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\SchemaData;
15ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\Validator;
16ba766201SAndreas Gohruse dokuwiki\plugin\struct\meta\Value;
17c2fd0bf0SMichael Große
183c2e6844SAndreas Gohr/**
193c2e6844SAndreas Gohr * Class action_plugin_struct_entry
203c2e6844SAndreas Gohr *
213c2e6844SAndreas Gohr * Handles the whole struct data entry process
223c2e6844SAndreas Gohr */
23549a0837SAndreas Gohrclass action_plugin_struct_entry extends DokuWiki_Action_Plugin {
24549a0837SAndreas Gohr
2517560ecbSAndreas Gohr    /**
2617560ecbSAndreas Gohr     * @var string The form name we use to transfer schema data
2717560ecbSAndreas Gohr     */
2817560ecbSAndreas Gohr    protected static $VAR = 'struct_schema_data';
29c2fd0bf0SMichael Große
30c2fd0bf0SMichael Große    /** @var helper_plugin_sqlite */
31c2fd0bf0SMichael Große    protected $sqlite;
32c2fd0bf0SMichael Große
333c2e6844SAndreas Gohr    /** @var  bool has the data been validated correctly? */
343c2e6844SAndreas Gohr    protected $validated;
353c2e6844SAndreas Gohr
363c2e6844SAndreas Gohr    /** @var  array these schemas have changed data and need to be saved */
373c2e6844SAndreas Gohr    protected $tosave;
383c2e6844SAndreas Gohr
39549a0837SAndreas Gohr    /**
40549a0837SAndreas Gohr     * Registers a callback function for a given event
41549a0837SAndreas Gohr     *
42549a0837SAndreas Gohr     * @param Doku_Event_Handler $controller DokuWiki's event controller object
43549a0837SAndreas Gohr     * @return void
44549a0837SAndreas Gohr     */
45549a0837SAndreas Gohr    public function register(Doku_Event_Handler $controller) {
463c2e6844SAndreas Gohr        // add the struct editor to the edit form;
47c2fd0bf0SMichael Große        $controller->register_hook('HTML_EDITFORM_OUTPUT', 'BEFORE', $this, 'handle_editform');
483c2e6844SAndreas Gohr        // validate data on preview and save;
493c2e6844SAndreas Gohr        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_validation');
503c2e6844SAndreas Gohr        // ensure a page revision is created when struct data changes:
513c2e6844SAndreas Gohr        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'BEFORE', $this, 'handle_pagesave_before');
523c2e6844SAndreas Gohr        // save struct data after page has been saved:
533c2e6844SAndreas Gohr        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'AFTER', $this, 'handle_pagesave_after');
54549a0837SAndreas Gohr    }
55549a0837SAndreas Gohr
56549a0837SAndreas Gohr    /**
573c2e6844SAndreas Gohr     * Enhance the editing form with structural data editing
5804641d56SMichael Große     *
5904641d56SMichael Große     * @param Doku_Event $event event object by reference
6004641d56SMichael Große     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
6104641d56SMichael Große     *                           handler was registered]
6204641d56SMichael Große     * @return bool
6304641d56SMichael Große     */
643c2e6844SAndreas Gohr    public function handle_editform(Doku_Event $event, $param) {
653c2e6844SAndreas Gohr        global $ID;
663c2e6844SAndreas Gohr
673c2e6844SAndreas Gohr        $assignments = new Assignments();
683c2e6844SAndreas Gohr        $tables = $assignments->getPageAssignments($ID);
693c2e6844SAndreas Gohr
703c2e6844SAndreas Gohr        $html = '';
713c2e6844SAndreas Gohr        foreach($tables as $table) {
723c2e6844SAndreas Gohr            $html .= $this->createForm($table);
733c2e6844SAndreas Gohr        }
743c2e6844SAndreas Gohr
753c2e6844SAndreas Gohr        /** @var Doku_Form $form */
763c2e6844SAndreas Gohr        $form = $event->data;
776b5e52fdSAndreas Gohr        $html = "<div class=\"struct_entry_form\">$html</div>";
783c2e6844SAndreas Gohr        $pos = $form->findElementById('wiki__editbar'); // insert the form before the main buttons
793c2e6844SAndreas Gohr        $form->insertElement($pos, $html);
803c2e6844SAndreas Gohr
813c2e6844SAndreas Gohr        return true;
823c2e6844SAndreas Gohr    }
833c2e6844SAndreas Gohr
843c2e6844SAndreas Gohr    /**
853c2e6844SAndreas Gohr     * Clean up and validate the input data
863c2e6844SAndreas Gohr     *
873c2e6844SAndreas Gohr     * @param Doku_Event $event event object by reference
883c2e6844SAndreas Gohr     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
893c2e6844SAndreas Gohr     *                           handler was registered]
903c2e6844SAndreas Gohr     * @return bool
913c2e6844SAndreas Gohr     */
923c2e6844SAndreas Gohr    public function handle_validation(Doku_Event $event, $param) {
933ece9074SMichael Große        global $ID, $INPUT;
9417560ecbSAndreas Gohr        $act = act_clean($event->data);
9517560ecbSAndreas Gohr        if(!in_array($act, array('save', 'preview'))) return false;
9604641d56SMichael Große
97c8a548a8SAndreas Gohr        // execute the validator
98c8a548a8SAndreas Gohr        $validator = new Validator();
99c8a548a8SAndreas Gohr        $this->validated = $validator->validate($INPUT->arr(self::$VAR), $ID);
100c8a548a8SAndreas Gohr        $this->tosave = $validator->getChangedSchemas();
101c8a548a8SAndreas Gohr        $INPUT->post->set(self::$VAR, $validator->getCleanedData());
10204641d56SMichael Große
103c8a548a8SAndreas Gohr        if(!$this->validated) foreach($validator->getErrors() as $error) {
104c8a548a8SAndreas Gohr            msg(hsc($error), -1);
105bd92cd68SAndreas Gohr        }
106bd92cd68SAndreas Gohr
10717560ecbSAndreas Gohr        // did validation go through? otherwise abort saving
1083c2e6844SAndreas Gohr        if(!$this->validated && $act == 'save') {
10917560ecbSAndreas Gohr            $event->data = 'edit';
11017560ecbSAndreas Gohr        }
11117560ecbSAndreas Gohr
11204641d56SMichael Große        return false;
11304641d56SMichael Große    }
11404641d56SMichael Große
11517560ecbSAndreas Gohr    /**
1163c2e6844SAndreas Gohr     * Check if the page has to be changed
1173c2e6844SAndreas Gohr     *
1183c2e6844SAndreas Gohr     * @param Doku_Event $event event object by reference
1193c2e6844SAndreas Gohr     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
1203c2e6844SAndreas Gohr     *                           handler was registered]
1213c2e6844SAndreas Gohr     * @return bool
1223c2e6844SAndreas Gohr     */
1233c2e6844SAndreas Gohr    public function handle_pagesave_before(Doku_Event $event, $param) {
1243c2e6844SAndreas Gohr        if($event->data['contentChanged']) return; // will be saved for page changes
125d683a527SAndreas Gohr        global $ACT;
126d683a527SAndreas Gohr        global $REV;
1277dac04ffSAndreas Gohr
1287dac04ffSAndreas Gohr        if(count($this->tosave) || isset($GLOBALS['struct_plugin_force_page_save'])) {
1293c2e6844SAndreas Gohr            if(trim($event->data['newContent']) === '') {
1303c2e6844SAndreas Gohr                // this happens when a new page is tried to be created with only struct data
1313c2e6844SAndreas Gohr                msg($this->getLang('emptypage'), -1);
1323c2e6844SAndreas Gohr            } else {
1333c2e6844SAndreas Gohr                $event->data['contentChanged'] = true; // save for data changes
13448010be8SAndreas Gohr
13548010be8SAndreas Gohr                // add a summary
13648010be8SAndreas Gohr                if(empty($event->data['summary'])) {
13748010be8SAndreas Gohr                    $event->data['summary'] = $this->getLang('summary');
13848010be8SAndreas Gohr                }
1393c2e6844SAndreas Gohr            }
140d683a527SAndreas Gohr        } else if($ACT == 'revert' && $REV) {
141d683a527SAndreas Gohr            // revert actions are not validated, so we need to force changes extra
142d683a527SAndreas Gohr            $assignments = new Assignments();
143d683a527SAndreas Gohr            $tosave = $assignments->getPageAssignments($event->data['id']);
144d683a527SAndreas Gohr            if(count($tosave)) {
145d683a527SAndreas Gohr                $event->data['contentChanged'] = true; // save for data changes
146d683a527SAndreas Gohr            }
1473c2e6844SAndreas Gohr        }
1483c2e6844SAndreas Gohr    }
1493c2e6844SAndreas Gohr
1503c2e6844SAndreas Gohr    /**
1513c2e6844SAndreas Gohr     * Save the data
1523c2e6844SAndreas Gohr     *
15356672c36SAndreas Gohr     * When this is called, INPUT data has been validated already. On a restore action, the data is
15456672c36SAndreas Gohr     * loaded from the database and not validated again.
15556672c36SAndreas Gohr     *
1563c2e6844SAndreas Gohr     * @param Doku_Event $event event object by reference
1573c2e6844SAndreas Gohr     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
1583c2e6844SAndreas Gohr     *                           handler was registered]
1593c2e6844SAndreas Gohr     * @return bool
1603c2e6844SAndreas Gohr     */
1613c2e6844SAndreas Gohr    public function handle_pagesave_after(Doku_Event $event, $param) {
1623c2e6844SAndreas Gohr        global $INPUT;
16356672c36SAndreas Gohr        global $ACT;
16456672c36SAndreas Gohr        global $REV;
1653c2e6844SAndreas Gohr
166956fa7d4SAndreas Gohr        $assignments = new Assignments();
167956fa7d4SAndreas Gohr
16856672c36SAndreas Gohr        if($ACT == 'revert' && $REV) {
16956672c36SAndreas Gohr            // reversion is a special case, we load the data to restore from DB:
17056672c36SAndreas Gohr            $structData = array();
17156672c36SAndreas Gohr            $this->tosave = $assignments->getPageAssignments($event->data['id']);
17256672c36SAndreas Gohr            foreach($this->tosave as $table) {
173*f411d872SAndreas Gohr                $oldData = AccessTable::byTableName($table, $event->data['id'], $REV);
174bf4e3880SAndreas Gohr                $oldData->optionRawValue(true);
17556672c36SAndreas Gohr                $structData[$table] = $oldData->getDataArray();
17656672c36SAndreas Gohr            }
17756672c36SAndreas Gohr        } else {
17856672c36SAndreas Gohr            // data comes from the edit form
1793c2e6844SAndreas Gohr            $structData = $INPUT->arr(self::$VAR);
18056672c36SAndreas Gohr        }
1813c2e6844SAndreas Gohr
18258cb1498SAndreas Gohr        if($event->data['changeType'] == DOKU_CHANGE_TYPE_DELETE && empty($GLOBALS['PLUGIN_MOVE_WORKING'])) {
18358cb1498SAndreas Gohr            // clear all data on delete unless it's a move operation
184eeb8d29fSAndreas Gohr            $tables = $assignments->getPageAssignments($event->data['id']);
185eeb8d29fSAndreas Gohr            foreach($tables as $table) {
186*f411d872SAndreas Gohr                $schemaData = AccessTable::byTableName($table, $event->data['id'], time());
187eeb8d29fSAndreas Gohr                $schemaData->clearData();
1883c2e6844SAndreas Gohr            }
189eeb8d29fSAndreas Gohr        } else {
190eeb8d29fSAndreas Gohr            // save the provided data
1917b9717bfSAndreas Gohr            if($this->tosave) foreach($this->tosave as $table) {
192*f411d872SAndreas Gohr                $schemaData = AccessTable::byTableName($table, $event->data['id'], $event->data['newRevision']);
1933c2e6844SAndreas Gohr                $schemaData->saveData($structData[$table]);
194956fa7d4SAndreas Gohr
195956fa7d4SAndreas Gohr                // make sure this schema is assigned
196956fa7d4SAndreas Gohr                $assignments->assignPageSchema($event->data['id'], $table);
1973c2e6844SAndreas Gohr            }
1983c2e6844SAndreas Gohr        }
199eeb8d29fSAndreas Gohr    }
2003c2e6844SAndreas Gohr
2013c2e6844SAndreas Gohr    /**
20265598e4aSAndreas Gohr     * Create the form to edit schemadata
203f36fda9dSAndreas Gohr     *
204c2fd0bf0SMichael Große     * @param string $tablename
20565598e4aSAndreas Gohr     * @return string The HTML for this schema's form
206c2fd0bf0SMichael Große     */
20765598e4aSAndreas Gohr    protected function createForm($tablename) {
208c2fd0bf0SMichael Große        global $ID;
20983beda18SAndreas Gohr        global $REV;
21017560ecbSAndreas Gohr        global $INPUT;
211fc13e8e7SMichael Große        if (auth_quickaclcheck($ID) == AUTH_READ) return '';
212cb249c51SMichael Große        if (checklock($ID)) return '';
213*f411d872SAndreas Gohr        $schema = AccessTable::byTableName($tablename, $ID, $REV);
214c2fd0bf0SMichael Große        $schemadata = $schema->getData();
215c2fd0bf0SMichael Große
21617560ecbSAndreas Gohr        $structdata = $INPUT->arr(self::$VAR);
21717560ecbSAndreas Gohr        if(isset($structdata[$tablename])) {
21817560ecbSAndreas Gohr            $postdata = $structdata[$tablename];
21917560ecbSAndreas Gohr        } else {
22017560ecbSAndreas Gohr            $postdata = array();
22117560ecbSAndreas Gohr        }
22217560ecbSAndreas Gohr
2235d5ff984SAndreas Gohr        // we need a short, unique identifier to use in the cookie. this should be good enough
2245d5ff984SAndreas Gohr        $schemaid = 'SRCT'.substr(str_replace(array('+', '/'), '', base64_encode(sha1($tablename, true))), 0, 5);
2255d5ff984SAndreas Gohr        $html = '<fieldset data-schema="' . $schemaid . '">';
22634bdbea7SAndreas Gohr        $html .= '<legend>' . hsc($tablename) . '</legend>';
227053212b1SAndreas Gohr        foreach($schemadata as $field) {
228053212b1SAndreas Gohr            $label = $field->getColumn()->getLabel();
22917560ecbSAndreas Gohr            if(isset($postdata[$label])) {
23017560ecbSAndreas Gohr                // posted data trumps stored data
23117560ecbSAndreas Gohr                $field->setValue($postdata[$label]);
23217560ecbSAndreas Gohr            }
23395838d50SAndreas Gohr            $html .=  $this->makeField($field, self::$VAR . "[$tablename][$label]");
234ed3de3d6SAndreas Gohr        }
235ed3de3d6SAndreas Gohr        $html .= '</fieldset>';
236ed3de3d6SAndreas Gohr
237ed3de3d6SAndreas Gohr        return $html;
238ed3de3d6SAndreas Gohr    }
239ed3de3d6SAndreas Gohr
240ed3de3d6SAndreas Gohr    /**
241ed3de3d6SAndreas Gohr     * Create the input field
242ed3de3d6SAndreas Gohr     *
243ed3de3d6SAndreas Gohr     * @param Value $field
244ed3de3d6SAndreas Gohr     * @param String $name field's name
245ed3de3d6SAndreas Gohr     * @return string
246ed3de3d6SAndreas Gohr     */
2476b5e52fdSAndreas Gohr    public function makeField(Value $field, $name) {
2489e9bee91SAndreas Gohr        $trans = hsc($field->getColumn()->getTranslatedLabel());
2493a717675SAndreas Gohr        $hint  = hsc($field->getColumn()->getTranslatedHint());
2503a717675SAndreas Gohr        $class = $hint ? 'hashint' : '';
251ed3de3d6SAndreas Gohr        $colname = $field->getColumn()->getFullQualifiedLabel();
2523a717675SAndreas Gohr
253053212b1SAndreas Gohr        $input = $field->getValueEditor($name);
2542350b48eSAndreas Gohr
2552350b48eSAndreas Gohr        // we keep all the custom form stuff the field might produce, but hide it
2562350b48eSAndreas Gohr        if(!$field->getColumn()->isVisibleInEditor()) {
2572350b48eSAndreas Gohr            $hide = 'style="display:none"';
2582350b48eSAndreas Gohr        } else {
2592350b48eSAndreas Gohr            $hide = '';
2602350b48eSAndreas Gohr        }
2612350b48eSAndreas Gohr
262ed3de3d6SAndreas Gohr        $html = '';
263ed3de3d6SAndreas Gohr        $html .= "<label $hide data-column=\"$colname\">";
2643a717675SAndreas Gohr        $html .= "<span class=\"label $class\" title=\"$hint\">$trans</span>";
2653a717675SAndreas Gohr        $html .= "<span class=\"input\">$input</span>";
2663a717675SAndreas Gohr        $html .= '</label>';
26765598e4aSAndreas Gohr
26865598e4aSAndreas Gohr        return $html;
269549a0837SAndreas Gohr    }
270549a0837SAndreas Gohr}
271549a0837SAndreas Gohr
272549a0837SAndreas Gohr// vim:ts=4:sw=4:et:
273