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 } 119*6742cd51SAndreas Gohr // strip empty fields from multi vals 120*6742cd51SAndreas Gohr if(is_array($newData[$label])) { 121*6742cd51SAndreas Gohr $newData[$label] = array_filter($newData[$label], array($this,'filter')); 122*6742cd51SAndreas Gohr $newData[$label] = array_values($newData[$label]); // reset the array keys 123*6742cd51SAndreas 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 global $lang; 1593c2e6844SAndreas Gohr 1603c2e6844SAndreas Gohr if($event->data['contentChanged']) return; // will be saved for page changes 1613c2e6844SAndreas Gohr if(count($this->tosave)) { 1623c2e6844SAndreas Gohr if(trim($event->data['newContent']) === '') { 1633c2e6844SAndreas Gohr // this happens when a new page is tried to be created with only struct data 1643c2e6844SAndreas Gohr msg($this->getLang('emptypage'), -1); 1653c2e6844SAndreas Gohr } else { 1663c2e6844SAndreas Gohr $event->data['contentChanged'] = true; // save for data changes 1673c2e6844SAndreas Gohr } 1683c2e6844SAndreas Gohr } 1693c2e6844SAndreas Gohr } 1703c2e6844SAndreas Gohr 1713c2e6844SAndreas Gohr /** 1723c2e6844SAndreas Gohr * Save the data 1733c2e6844SAndreas Gohr * 1743c2e6844SAndreas Gohr * @param Doku_Event $event event object by reference 1753c2e6844SAndreas Gohr * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 1763c2e6844SAndreas Gohr * handler was registered] 1773c2e6844SAndreas Gohr * @return bool 1783c2e6844SAndreas Gohr */ 1793c2e6844SAndreas Gohr public function handle_pagesave_after(Doku_Event $event, $param) { 1803c2e6844SAndreas Gohr global $INPUT; 1813c2e6844SAndreas Gohr 1823c2e6844SAndreas Gohr // this should never happen, because the validation is checked in handle_validation already 1833c2e6844SAndreas Gohr if(!$this->validated) return; 1843c2e6844SAndreas Gohr 1853c2e6844SAndreas Gohr if($event->data['changeType'] == DOKU_CHANGE_TYPE_DELETE) { 1863c2e6844SAndreas Gohr // FIXME we should probably clean out all data on delete!? 1873c2e6844SAndreas Gohr return; 1883c2e6844SAndreas Gohr } 1893c2e6844SAndreas Gohr 1903c2e6844SAndreas Gohr $structData = $INPUT->arr(self::$VAR); 1913c2e6844SAndreas Gohr 1923c2e6844SAndreas Gohr foreach($this->tosave as $table) { 1933c2e6844SAndreas Gohr $schemaData = new SchemaData($table, $event->data['id'], $event->data['newRevision']); 1943c2e6844SAndreas Gohr $schemaData->saveData($structData[$table]); 1953c2e6844SAndreas Gohr } 1963c2e6844SAndreas Gohr } 1973c2e6844SAndreas Gohr 1983c2e6844SAndreas Gohr /** 19917560ecbSAndreas Gohr * Validate the given data 20017560ecbSAndreas Gohr * 20117560ecbSAndreas Gohr * Catches the Validation exceptions and transforms them into proper messages. 20217560ecbSAndreas Gohr * 20317560ecbSAndreas Gohr * Blank values are not validated and always pass 20417560ecbSAndreas Gohr * 20517560ecbSAndreas Gohr * @param AbstractBaseType $type 20617560ecbSAndreas Gohr * @param string $label 20717560ecbSAndreas Gohr * @param array|string|int $data 20817560ecbSAndreas Gohr * @return bool true if the data validates, otherwise false 20917560ecbSAndreas Gohr */ 21017560ecbSAndreas Gohr protected function validate(AbstractBaseType $type, $label, $data) { 21117560ecbSAndreas Gohr $prefix = sprintf($this->getLang('validation_prefix'), $label); 21217560ecbSAndreas Gohr 21317560ecbSAndreas Gohr $ok = true; 21417560ecbSAndreas Gohr if(is_array($data)) { 21517560ecbSAndreas Gohr foreach($data as $value) { 21617560ecbSAndreas Gohr if(!blank($value)) { 21717560ecbSAndreas Gohr try { 21817560ecbSAndreas Gohr $type->validate($value); 21917560ecbSAndreas Gohr } catch(ValidationException $e) { 22017560ecbSAndreas Gohr msg($prefix . $e->getMessage(), -1); 22117560ecbSAndreas Gohr $ok = false; 22217560ecbSAndreas Gohr } 22317560ecbSAndreas Gohr } 22417560ecbSAndreas Gohr } 2254f443211SMichael Große return $ok; 2264f443211SMichael Große } 2274f443211SMichael Große 22817560ecbSAndreas Gohr if(!blank($data)) { 22917560ecbSAndreas Gohr try { 23017560ecbSAndreas Gohr $type->validate($data); 23117560ecbSAndreas Gohr } catch(ValidationException $e) { 23217560ecbSAndreas Gohr msg($prefix . $e->getMessage(), -1); 23317560ecbSAndreas Gohr $ok = false; 23417560ecbSAndreas Gohr } 23517560ecbSAndreas Gohr } 23617560ecbSAndreas Gohr return $ok; 23717560ecbSAndreas Gohr } 23817560ecbSAndreas Gohr 239c2fd0bf0SMichael Große /** 24065598e4aSAndreas Gohr * Create the form to edit schemadata 241f36fda9dSAndreas Gohr * 242c2fd0bf0SMichael Große * @param string $tablename 24365598e4aSAndreas Gohr * @return string The HTML for this schema's form 244c2fd0bf0SMichael Große */ 24565598e4aSAndreas Gohr protected function createForm($tablename) { 246c2fd0bf0SMichael Große global $ID; 24783beda18SAndreas Gohr global $REV; 24817560ecbSAndreas Gohr global $INPUT; 24983beda18SAndreas Gohr $schema = new SchemaData($tablename, $ID, $REV); 250c2fd0bf0SMichael Große $schemadata = $schema->getData(); 251c2fd0bf0SMichael Große 25217560ecbSAndreas Gohr $structdata = $INPUT->arr(self::$VAR); 25317560ecbSAndreas Gohr if(isset($structdata[$tablename])) { 25417560ecbSAndreas Gohr $postdata = $structdata[$tablename]; 25517560ecbSAndreas Gohr } else { 25617560ecbSAndreas Gohr $postdata = array(); 25717560ecbSAndreas Gohr } 25817560ecbSAndreas Gohr 25934bdbea7SAndreas Gohr $html = '<fieldset>'; 26034bdbea7SAndreas Gohr $html .= '<legend>'.hsc($tablename).'</legend>'; 261053212b1SAndreas Gohr foreach($schemadata as $field) { 262053212b1SAndreas Gohr $label = $field->getColumn()->getLabel(); 26317560ecbSAndreas Gohr if(isset($postdata[$label])) { 26417560ecbSAndreas Gohr // posted data trumps stored data 26517560ecbSAndreas Gohr $field->setValue($postdata[$label]); 26617560ecbSAndreas Gohr } 2679e9bee91SAndreas Gohr $trans = hsc($field->getColumn()->getTranslatedLabel()); 26817560ecbSAndreas Gohr $name = self::$VAR . "[$tablename][$label]"; 269053212b1SAndreas Gohr $input = $field->getValueEditor($name); 27034bdbea7SAndreas Gohr $html .= "<label><span class=\"label\">$trans</span><div class=\"input\">$input</div></label>"; 271c2fd0bf0SMichael Große } 27234bdbea7SAndreas Gohr $html .= '</fieldset>'; 27365598e4aSAndreas Gohr 27465598e4aSAndreas Gohr return $html; 275549a0837SAndreas Gohr } 276549a0837SAndreas Gohr 277*6742cd51SAndreas Gohr /** 278*6742cd51SAndreas Gohr * Simple filter to remove blank values 279*6742cd51SAndreas Gohr * 280*6742cd51SAndreas Gohr * @param string $val 281*6742cd51SAndreas Gohr * @return bool 282*6742cd51SAndreas Gohr */ 283*6742cd51SAndreas Gohr public function filter($val) { 284*6742cd51SAndreas Gohr return !blank($val); 285*6742cd51SAndreas Gohr } 286549a0837SAndreas Gohr} 287549a0837SAndreas Gohr 288549a0837SAndreas Gohr// vim:ts=4:sw=4:et: 289