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