xref: /plugin/struct/action/entry.php (revision 956fa7d4547b33dd5297bb3b6ed60e950b22ee52)
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
12fb31ca9fSAndreas Gohruse plugin\struct\meta\Assignments;
13c2fd0bf0SMichael Großeuse plugin\struct\meta\SchemaData;
1417560ecbSAndreas Gohruse plugin\struct\meta\ValidationException;
1517560ecbSAndreas Gohruse plugin\struct\types\AbstractBaseType;
16c2fd0bf0SMichael Große
173c2e6844SAndreas Gohr/**
183c2e6844SAndreas Gohr * Class action_plugin_struct_entry
193c2e6844SAndreas Gohr *
203c2e6844SAndreas Gohr * Handles the whole struct data entry process
213c2e6844SAndreas Gohr */
22549a0837SAndreas Gohrclass action_plugin_struct_entry extends DokuWiki_Action_Plugin {
23549a0837SAndreas Gohr
2417560ecbSAndreas Gohr    /**
2517560ecbSAndreas Gohr     * @var string The form name we use to transfer schema data
2617560ecbSAndreas Gohr     */
2717560ecbSAndreas Gohr    protected static $VAR = 'struct_schema_data';
28c2fd0bf0SMichael Große
29c2fd0bf0SMichael Große    /** @var helper_plugin_sqlite */
30c2fd0bf0SMichael Große    protected $sqlite;
31c2fd0bf0SMichael Große
323c2e6844SAndreas Gohr    /** @var  bool has the data been validated correctly? */
333c2e6844SAndreas Gohr    protected $validated;
343c2e6844SAndreas Gohr
353c2e6844SAndreas Gohr    /** @var  array these schemas have changed data and need to be saved */
363c2e6844SAndreas Gohr    protected $tosave;
373c2e6844SAndreas Gohr
38549a0837SAndreas Gohr    /**
39549a0837SAndreas Gohr     * Registers a callback function for a given event
40549a0837SAndreas Gohr     *
41549a0837SAndreas Gohr     * @param Doku_Event_Handler $controller DokuWiki's event controller object
42549a0837SAndreas Gohr     * @return void
43549a0837SAndreas Gohr     */
44549a0837SAndreas Gohr    public function register(Doku_Event_Handler $controller) {
453c2e6844SAndreas Gohr        // add the struct editor to the edit form;
46c2fd0bf0SMichael Große        $controller->register_hook('HTML_EDITFORM_OUTPUT', 'BEFORE', $this, 'handle_editform');
473c2e6844SAndreas Gohr        // validate data on preview and save;
483c2e6844SAndreas Gohr        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_validation');
493c2e6844SAndreas Gohr        // ensure a page revision is created when struct data changes:
503c2e6844SAndreas Gohr        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'BEFORE', $this, 'handle_pagesave_before');
513c2e6844SAndreas Gohr        // save struct data after page has been saved:
523c2e6844SAndreas Gohr        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'AFTER', $this, 'handle_pagesave_after');
53549a0837SAndreas Gohr    }
54549a0837SAndreas Gohr
55549a0837SAndreas Gohr    /**
563c2e6844SAndreas Gohr     * Enhance the editing form with structural data editing
5704641d56SMichael Große     *
5804641d56SMichael Große     * @param Doku_Event $event event object by reference
5904641d56SMichael Große     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
6004641d56SMichael Große     *                           handler was registered]
6104641d56SMichael Große     * @return bool
6204641d56SMichael Große     */
633c2e6844SAndreas Gohr    public function handle_editform(Doku_Event $event, $param) {
643c2e6844SAndreas Gohr        global $ID;
653c2e6844SAndreas Gohr
663c2e6844SAndreas Gohr        $assignments = new Assignments();
673c2e6844SAndreas Gohr        $tables = $assignments->getPageAssignments($ID);
683c2e6844SAndreas Gohr
693c2e6844SAndreas Gohr        $html = '';
703c2e6844SAndreas Gohr        foreach($tables as $table) {
713c2e6844SAndreas Gohr            $html .= $this->createForm($table);
723c2e6844SAndreas Gohr        }
733c2e6844SAndreas Gohr
743c2e6844SAndreas Gohr        /** @var Doku_Form $form */
753c2e6844SAndreas Gohr        $form = $event->data;
763c2e6844SAndreas Gohr        $html = "<div class=\"struct\">$html</div>";
773c2e6844SAndreas Gohr        $pos = $form->findElementById('wiki__editbar'); // insert the form before the main buttons
783c2e6844SAndreas Gohr        $form->insertElement($pos, $html);
793c2e6844SAndreas Gohr
803c2e6844SAndreas Gohr        return true;
813c2e6844SAndreas Gohr    }
823c2e6844SAndreas Gohr
833c2e6844SAndreas Gohr    /**
843c2e6844SAndreas Gohr     * Clean up and validate the input data
853c2e6844SAndreas Gohr     *
863c2e6844SAndreas Gohr     * @param Doku_Event $event event object by reference
873c2e6844SAndreas Gohr     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
883c2e6844SAndreas Gohr     *                           handler was registered]
893c2e6844SAndreas Gohr     * @return bool
903c2e6844SAndreas Gohr     */
913c2e6844SAndreas Gohr    public function handle_validation(Doku_Event $event, $param) {
923ece9074SMichael Große        global $ID, $INPUT;
9317560ecbSAndreas Gohr        $act = act_clean($event->data);
9417560ecbSAndreas Gohr        if(!in_array($act, array('save', 'preview'))) return false;
9504641d56SMichael Große
96a8b9fb5bSAndreas Gohr        $assignments = new Assignments();
97a8b9fb5bSAndreas Gohr        $tables = $assignments->getPageAssignments($ID);
9817560ecbSAndreas Gohr        $structData = $INPUT->arr(self::$VAR);
993c2e6844SAndreas Gohr        $timestamp = time();
10004641d56SMichael Große
1013c2e6844SAndreas Gohr        $this->tosave = array();
1023c2e6844SAndreas Gohr        $this->validated = true;
10304641d56SMichael Große        foreach($tables as $table) {
1043c2e6844SAndreas Gohr            $schemaData = new SchemaData($table, $ID, $timestamp);
1053c2e6844SAndreas Gohr            if(!$schemaData->getId()) {
106bd92cd68SAndreas Gohr                // this schema is not available for some reason. skip it
107bd92cd68SAndreas Gohr                continue;
108bd92cd68SAndreas Gohr            }
109bd92cd68SAndreas Gohr
1103c2e6844SAndreas Gohr            $newData = $structData[$table];
1113c2e6844SAndreas Gohr            foreach($schemaData->getColumns() as $col) {
11217560ecbSAndreas Gohr                // fix multi value types
113afbd4e60SMichael Große                $type = $col->getType();
114afbd4e60SMichael Große                $label = $type->getLabel();
11517560ecbSAndreas Gohr                $trans = $type->getTranslatedLabel();
1163c2e6844SAndreas Gohr                if($type->isMulti() && !is_array($newData[$label])) {
1173c2e6844SAndreas Gohr                    $newData[$label] = $type->splitValues($newData[$label]);
11804641d56SMichael Große                }
1196742cd51SAndreas Gohr                // strip empty fields from multi vals
1206742cd51SAndreas Gohr                if(is_array($newData[$label])) {
1216742cd51SAndreas Gohr                    $newData[$label] = array_filter($newData[$label], array($this, 'filter'));
1226742cd51SAndreas Gohr                    $newData[$label] = array_values($newData[$label]); // reset the array keys
1236742cd51SAndreas Gohr                }
12417560ecbSAndreas Gohr
12517560ecbSAndreas Gohr                // validate data
126b8a9b73cSMichael Große                $this->validated = $this->validated && $this->validate($type, $trans, $newData[$label]);
12704641d56SMichael Große            }
12817560ecbSAndreas Gohr
1293c2e6844SAndreas Gohr            // has the data changed? mark it for saving.
1303c2e6844SAndreas Gohr            $olddata = $schemaData->getDataArray();
1313c2e6844SAndreas Gohr            if($olddata != $newData) {
1323c2e6844SAndreas Gohr                $this->tosave[] = $table;
13304641d56SMichael Große            }
13417560ecbSAndreas Gohr
1353c2e6844SAndreas Gohr            // write back cleaned up data
1363c2e6844SAndreas Gohr            $structData[$table] = $newData;
13717560ecbSAndreas Gohr        }
13817560ecbSAndreas Gohr        // write back cleaned up structData
1393c2e6844SAndreas Gohr        $INPUT->post->set(self::$VAR, $structData);
14017560ecbSAndreas Gohr
14117560ecbSAndreas Gohr        // did validation go through? otherwise abort saving
1423c2e6844SAndreas Gohr        if(!$this->validated && $act == 'save') {
14317560ecbSAndreas Gohr            $event->data = 'edit';
14417560ecbSAndreas Gohr        }
14517560ecbSAndreas Gohr
14604641d56SMichael Große        return false;
14704641d56SMichael Große    }
14804641d56SMichael Große
14917560ecbSAndreas Gohr    /**
1503c2e6844SAndreas Gohr     * Check if the page has to be changed
1513c2e6844SAndreas Gohr     *
1523c2e6844SAndreas Gohr     * @param Doku_Event $event event object by reference
1533c2e6844SAndreas Gohr     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
1543c2e6844SAndreas Gohr     *                           handler was registered]
1553c2e6844SAndreas Gohr     * @return bool
1563c2e6844SAndreas Gohr     */
1573c2e6844SAndreas Gohr    public function handle_pagesave_before(Doku_Event $event, $param) {
1583c2e6844SAndreas Gohr        if($event->data['contentChanged']) return; // will be saved for page changes
1593c2e6844SAndreas Gohr        if(count($this->tosave)) {
1603c2e6844SAndreas Gohr            if(trim($event->data['newContent']) === '') {
1613c2e6844SAndreas Gohr                // this happens when a new page is tried to be created with only struct data
1623c2e6844SAndreas Gohr                msg($this->getLang('emptypage'), -1);
1633c2e6844SAndreas Gohr            } else {
1643c2e6844SAndreas Gohr                $event->data['contentChanged'] = true; // save for data changes
1653c2e6844SAndreas Gohr            }
1663c2e6844SAndreas Gohr        }
1673c2e6844SAndreas Gohr    }
1683c2e6844SAndreas Gohr
1693c2e6844SAndreas Gohr    /**
1703c2e6844SAndreas Gohr     * Save the data
1713c2e6844SAndreas Gohr     *
17256672c36SAndreas Gohr     * When this is called, INPUT data has been validated already. On a restore action, the data is
17356672c36SAndreas Gohr     * loaded from the database and not validated again.
17456672c36SAndreas Gohr     *
1753c2e6844SAndreas Gohr     * @param Doku_Event $event event object by reference
1763c2e6844SAndreas Gohr     * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
1773c2e6844SAndreas Gohr     *                           handler was registered]
1783c2e6844SAndreas Gohr     * @return bool
1793c2e6844SAndreas Gohr     */
1803c2e6844SAndreas Gohr    public function handle_pagesave_after(Doku_Event $event, $param) {
1813c2e6844SAndreas Gohr        global $INPUT;
18256672c36SAndreas Gohr        global $ACT;
18356672c36SAndreas Gohr        global $REV;
1843c2e6844SAndreas Gohr
185*956fa7d4SAndreas Gohr        $assignments = new Assignments();
186*956fa7d4SAndreas Gohr
18756672c36SAndreas Gohr        if($ACT == 'revert' && $REV) {
18856672c36SAndreas Gohr            // reversion is a special case, we load the data to restore from DB:
18956672c36SAndreas Gohr            $structData = array();
19056672c36SAndreas Gohr            $this->tosave = $assignments->getPageAssignments($event->data['id']);
19156672c36SAndreas Gohr            foreach($this->tosave as $table) {
19256672c36SAndreas Gohr                $oldData = new SchemaData($table, $event->data['id'], $REV);
19356672c36SAndreas Gohr                $structData[$table] = $oldData->getDataArray();
19456672c36SAndreas Gohr            }
19556672c36SAndreas Gohr        } else {
19656672c36SAndreas Gohr            // data comes from the edit form
1973c2e6844SAndreas Gohr            $structData = $INPUT->arr(self::$VAR);
19856672c36SAndreas Gohr        }
1993c2e6844SAndreas Gohr
2003c2e6844SAndreas Gohr        if($event->data['changeType'] == DOKU_CHANGE_TYPE_DELETE) {
201eeb8d29fSAndreas Gohr            // clear all data
202eeb8d29fSAndreas Gohr            $tables = $assignments->getPageAssignments($event->data['id']);
203eeb8d29fSAndreas Gohr            foreach($tables as $table) {
204eeb8d29fSAndreas Gohr                $schemaData = new SchemaData($table, $event->data['id'], time());
205eeb8d29fSAndreas Gohr                $schemaData->clearData();
2063c2e6844SAndreas Gohr            }
207eeb8d29fSAndreas Gohr        } else {
208eeb8d29fSAndreas Gohr            // save the provided data
2093c2e6844SAndreas Gohr            foreach($this->tosave as $table) {
2103c2e6844SAndreas Gohr                $schemaData = new SchemaData($table, $event->data['id'], $event->data['newRevision']);
2113c2e6844SAndreas Gohr                $schemaData->saveData($structData[$table]);
212*956fa7d4SAndreas Gohr
213*956fa7d4SAndreas Gohr                // make sure this schema is assigned
214*956fa7d4SAndreas Gohr                $assignments->assignPageSchema($event->data['id'], $table);
2153c2e6844SAndreas Gohr            }
2163c2e6844SAndreas Gohr        }
217eeb8d29fSAndreas Gohr    }
2183c2e6844SAndreas Gohr
2193c2e6844SAndreas Gohr    /**
22017560ecbSAndreas Gohr     * Validate the given data
22117560ecbSAndreas Gohr     *
22217560ecbSAndreas Gohr     * Catches the Validation exceptions and transforms them into proper messages.
22317560ecbSAndreas Gohr     *
22417560ecbSAndreas Gohr     * Blank values are not validated and always pass
22517560ecbSAndreas Gohr     *
22617560ecbSAndreas Gohr     * @param AbstractBaseType $type
22717560ecbSAndreas Gohr     * @param string $label
22817560ecbSAndreas Gohr     * @param array|string|int $data
22917560ecbSAndreas Gohr     * @return bool true if the data validates, otherwise false
23017560ecbSAndreas Gohr     */
23117560ecbSAndreas Gohr    protected function validate(AbstractBaseType $type, $label, $data) {
23217560ecbSAndreas Gohr        $prefix = sprintf($this->getLang('validation_prefix'), $label);
23317560ecbSAndreas Gohr
23417560ecbSAndreas Gohr        $ok = true;
23517560ecbSAndreas Gohr        if(is_array($data)) {
23617560ecbSAndreas Gohr            foreach($data as $value) {
23717560ecbSAndreas Gohr                if(!blank($value)) {
23817560ecbSAndreas Gohr                    try {
23917560ecbSAndreas Gohr                        $type->validate($value);
24017560ecbSAndreas Gohr                    } catch(ValidationException $e) {
24117560ecbSAndreas Gohr                        msg($prefix . $e->getMessage(), -1);
24217560ecbSAndreas Gohr                        $ok = false;
24317560ecbSAndreas Gohr                    }
24417560ecbSAndreas Gohr                }
24517560ecbSAndreas Gohr            }
2464f443211SMichael Große            return $ok;
2474f443211SMichael Große        }
2484f443211SMichael Große
24917560ecbSAndreas Gohr        if(!blank($data)) {
25017560ecbSAndreas Gohr            try {
25117560ecbSAndreas Gohr                $type->validate($data);
25217560ecbSAndreas Gohr            } catch(ValidationException $e) {
25317560ecbSAndreas Gohr                msg($prefix . $e->getMessage(), -1);
25417560ecbSAndreas Gohr                $ok = false;
25517560ecbSAndreas Gohr            }
25617560ecbSAndreas Gohr        }
25717560ecbSAndreas Gohr        return $ok;
25817560ecbSAndreas Gohr    }
25917560ecbSAndreas Gohr
260c2fd0bf0SMichael Große    /**
26165598e4aSAndreas Gohr     * Create the form to edit schemadata
262f36fda9dSAndreas Gohr     *
263c2fd0bf0SMichael Große     * @param string $tablename
26465598e4aSAndreas Gohr     * @return string The HTML for this schema's form
265c2fd0bf0SMichael Große     */
26665598e4aSAndreas Gohr    protected function createForm($tablename) {
267c2fd0bf0SMichael Große        global $ID;
26883beda18SAndreas Gohr        global $REV;
26917560ecbSAndreas Gohr        global $INPUT;
27083beda18SAndreas Gohr        $schema = new SchemaData($tablename, $ID, $REV);
271c2fd0bf0SMichael Große        $schemadata = $schema->getData();
272c2fd0bf0SMichael Große
27317560ecbSAndreas Gohr        $structdata = $INPUT->arr(self::$VAR);
27417560ecbSAndreas Gohr        if(isset($structdata[$tablename])) {
27517560ecbSAndreas Gohr            $postdata = $structdata[$tablename];
27617560ecbSAndreas Gohr        } else {
27717560ecbSAndreas Gohr            $postdata = array();
27817560ecbSAndreas Gohr        }
27917560ecbSAndreas Gohr
28034bdbea7SAndreas Gohr        $html = '<fieldset>';
28134bdbea7SAndreas Gohr        $html .= '<legend>' . hsc($tablename) . '</legend>';
282053212b1SAndreas Gohr        foreach($schemadata as $field) {
283053212b1SAndreas Gohr            $label = $field->getColumn()->getLabel();
28417560ecbSAndreas Gohr            if(isset($postdata[$label])) {
28517560ecbSAndreas Gohr                // posted data trumps stored data
28617560ecbSAndreas Gohr                $field->setValue($postdata[$label]);
28717560ecbSAndreas Gohr            }
2889e9bee91SAndreas Gohr            $trans = hsc($field->getColumn()->getTranslatedLabel());
28917560ecbSAndreas Gohr            $name = self::$VAR . "[$tablename][$label]";
290053212b1SAndreas Gohr            $input = $field->getValueEditor($name);
291f94f4c4fSAndreas Gohr            $html .= "<label><span class=\"label\">$trans</span><span class=\"input\">$input</span></label>";
292c2fd0bf0SMichael Große        }
29334bdbea7SAndreas Gohr        $html .= '</fieldset>';
29465598e4aSAndreas Gohr
29565598e4aSAndreas Gohr        return $html;
296549a0837SAndreas Gohr    }
297549a0837SAndreas Gohr
2986742cd51SAndreas Gohr    /**
2996742cd51SAndreas Gohr     * Simple filter to remove blank values
3006742cd51SAndreas Gohr     *
3016742cd51SAndreas Gohr     * @param string $val
3026742cd51SAndreas Gohr     * @return bool
3036742cd51SAndreas Gohr     */
3046742cd51SAndreas Gohr    public function filter($val) {
3056742cd51SAndreas Gohr        return !blank($val);
3066742cd51SAndreas Gohr    }
307549a0837SAndreas Gohr}
308549a0837SAndreas Gohr
309549a0837SAndreas Gohr// vim:ts=4:sw=4:et:
310