register_hook(
'PLUGIN_USERSETTINGS_REGISTER',
'BEFORE',
$this,
'handleSettingsRegister'
);
// Inject annotation stats + user preference into JSINFO.
$controller->register_hook(
'TPL_METAHEADER_OUTPUT',
'BEFORE',
$this,
'handleMetaHeader'
);
// Handle the AJAX call.
$controller->register_hook(
'AJAX_CALL_UNKNOWN',
'BEFORE',
$this,
'handleAjax'
);
}
// ------------------------------------------------------------------
// 1. usersettings toggle registration
// ------------------------------------------------------------------
/**
* Append the annotations_enabled toggle definition to the event data.
*
* The event data is an array that the usersettings helper fires with
* createAndTrigger(); every handler appends its definition(s).
*
* @param Doku_Event $event PLUGIN_USERSETTINGS_REGISTER
* @param mixed $param
*/
public function handleSettingsRegister(Doku_Event $event, $param)
{
$event->data[] = [
'key' => 'annotations_enabled',
'label' => $this->getLang('toggle_label'),
'desc' => $this->getLang('toggle_desc'),
'type' => 'checkbox',
'default' => true,
'plugin' => 'annotations',
];
}
// ------------------------------------------------------------------
// 2. Inject into JSINFO
// ------------------------------------------------------------------
/**
* Add annotation stats and the user preference to JSINFO so script.js
* does not need an extra round-trip on page load.
*
* IMPORTANT: tpl_metaheaders() calls jsinfo() and then immediately
* JSON-encodes $JSINFO into an inline " would otherwise close the script element and
// inject arbitrary HTML — a stored XSS reachable by anyone who can
// annotate. HEX_TAG neutralises every tag-based breakout.
$payload = json_encode($data, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
// The inline script block containing "var JSINFO = ...;" is in
// $event->data['script']. Find it and append our assignment so it
// runs in the same scope after JSINFO is already declared.
if (!empty($event->data['script'])) {
foreach ($event->data['script'] as &$scriptTag) {
if (
isset($scriptTag['_data']) &&
strpos($scriptTag['_data'], 'var JSINFO') !== false
) {
$scriptTag['_data'] .= 'JSINFO.annotations=' . $payload . ';';
break;
}
}
unset($scriptTag);
}
}
/**
* Append a