*/
// must be run within Dokuwiki
use dokuwiki\plugin\struct\meta\AccessTable;
use dokuwiki\plugin\struct\meta\Column;
use dokuwiki\plugin\struct\meta\SchemaData;
use dokuwiki\plugin\struct\meta\StructException;
use dokuwiki\plugin\struct\meta\Validator;
if(!defined('DOKU_INC')) die();
/**
* Class action_plugin_struct_inline
*
* Handle inline editing
*/
class action_plugin_struct_inline extends DokuWiki_Action_Plugin {
/** @var SchemaData */
protected $schemadata = null;
/** @var Column */
protected $column = null;
/** @var String */
protected $pid = '';
/**
* Registers a callback function for a given event
*
* @param Doku_Event_Handler $controller DokuWiki's event controller object
* @return void
*/
public function register(Doku_Event_Handler $controller) {
$controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax');
}
/**
* @param Doku_Event $event
* @param $param
*/
public function handle_ajax(Doku_Event $event, $param) {
$len = strlen('plugin_struct_inline_');
if(substr($event->data, 0, $len) != 'plugin_struct_inline_') return;
$event->preventDefault();
$event->stopPropagation();
if(substr($event->data, $len) == 'editor') {
$this->inline_editor();
}
if(substr($event->data, $len) == 'save') {
try {
$this->inline_save();
} catch(StructException $e) {
http_status(500);
header('Content-Type: text/plain; charset=utf-8');
echo $e->getMessage();
}
}
if(substr($event->data, $len) == 'cancel') {
$this->inline_cancel();
}
}
/**
* Creates the inline editor
*/
protected function inline_editor() {
// silently fail when editing not possible
if(!$this->initFromInput()) return;
if(auth_quickaclcheck($this->pid) < AUTH_EDIT) return;
if(checklock($this->pid)) return;
// lock page
lock($this->pid);
// output the editor
$value = $this->schemadata->getDataColumn($this->column);
echo '';
$hint = $this->column->getType()->getTranslatedHint();
if($hint) {
echo '
';
echo hsc($hint);
echo '
';
}
// csrf protection
formSecurityToken();
}
/**
* Save the data posted by the inline editor
*/
protected function inline_save() {
global $INPUT;
if(!$this->initFromInput()) {
throw new StructException('inline save error: init');
}
// our own implementation of checkSecurityToken because we don't want the msg() call
if(
$INPUT->server->str('REMOTE_USER') &&
getSecurityToken() != $INPUT->str('sectok')
) {
throw new StructException('inline save error: csrf');
}
if(auth_quickaclcheck($this->pid) < AUTH_EDIT) {
throw new StructException('inline save error: acl');
}
if(checklock($this->pid)) {
throw new StructException('inline save error: lock');
}
// validate
$value = $INPUT->param('entry');
$validator = new Validator();
if(!$validator->validateValue($this->column, $value)) {
throw new StructException(join("\n", $validator->getErrors()));
}
// current data
$tosave = $this->schemadata->getDataArray();
$tosave[$this->column->getLabel()] = $value;
$tosave = array($this->schemadata->getSchema()->getTable() => $tosave);
// save
/** @var helper_plugin_struct $helper */
$helper = plugin_load('helper', 'struct');
$helper->saveData($this->pid, $tosave, 'inline edit');
// unlock
unlock($this->pid);
// reinit then render
$this->initFromInput();
$value = $this->schemadata->getDataColumn($this->column);
$R = new Doku_Renderer_xhtml();
$value->render($R, 'xhtml'); // FIXME use configured default renderer
echo $R->doc;
}
/**
* Unlock a page (on cancel action)
*/
protected function inline_cancel() {
global $INPUT;
$pid = $INPUT->str('pid');
unlock($pid);
}
/**
* Initialize internal state based on input variables
*
* @return bool if initialization was successfull
*/
protected function initFromInput() {
global $INPUT;
$this->schemadata = null;
$this->column = null;
$pid = $INPUT->str('pid');
list($table, $field) = explode('.', $INPUT->str('field'));
if(blank($pid)) return false;
if(blank($table)) return false;
if(blank($field)) return false;
$this->pid = $pid;
try {
$this->schemadata = AccessTable::byTableName($table, $pid);
} catch(StructException $ignore) {
return false;
}
$this->column = $this->schemadata->getSchema()->findColumn($field);
if(!$this->column || !$this->column->isVisibleInEditor()) {
$this->schemadata = null;
$this->column = null;
return false;
}
return true;
}
}