* add (disabled) shortcut syntax for plugin methods
* add controls shortcut for pluginhandler tags * add similar shortcut for frontend * allow plugins to selectively exclude their methods from CSRF checking
This commit is contained in:
parent
b16abc157e
commit
e4609c18ef
11
backend.php
11
backend.php
|
@ -88,6 +88,17 @@
|
||||||
5 => __("Power User"),
|
5 => __("Power User"),
|
||||||
10 => __("Administrator"));
|
10 => __("Administrator"));
|
||||||
|
|
||||||
|
// shortcut syntax for plugin methods (?op=plugin--pmethod&...params)
|
||||||
|
/* if (strpos($op, PluginHost::PUBLIC_METHOD_DELIMITER) !== false) {
|
||||||
|
list ($plugin, $pmethod) = explode(PluginHost::PUBLIC_METHOD_DELIMITER, $op, 2);
|
||||||
|
|
||||||
|
// TODO: better implementation that won't modify $_REQUEST
|
||||||
|
$_REQUEST["plugin"] = $plugin;
|
||||||
|
$method = $pmethod;
|
||||||
|
$op = "pluginhandler";
|
||||||
|
} */
|
||||||
|
|
||||||
|
// TODO: figure out if is this still needed
|
||||||
$op = str_replace("-", "_", $op);
|
$op = str_replace("-", "_", $op);
|
||||||
|
|
||||||
$override = PluginHost::getInstance()->lookup_handler($op, $method);
|
$override = PluginHost::getInstance()->lookup_handler($op, $method);
|
||||||
|
|
|
@ -54,4 +54,8 @@ abstract class Plugin {
|
||||||
|
|
||||||
return vsprintf($this->__($msgid), $args);
|
return vsprintf($this->__($msgid), $args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function csrf_ignore($method) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ class PluginHandler extends Handler_Protected {
|
||||||
|
|
||||||
if ($plugin) {
|
if ($plugin) {
|
||||||
if (method_exists($plugin, $method)) {
|
if (method_exists($plugin, $method)) {
|
||||||
if (validate_csrf($csrf_token)) {
|
if (validate_csrf($csrf_token) || $plugin->csrf_ignore($method)) {
|
||||||
$plugin->$method();
|
$plugin->$method();
|
||||||
} else {
|
} else {
|
||||||
user_error("Rejected ${plugin_name}->${method}(): invalid CSRF token.", E_USER_WARNING);
|
user_error("Rejected ${plugin_name}->${method}(): invalid CSRF token.", E_USER_WARNING);
|
||||||
|
|
|
@ -611,6 +611,17 @@ class PluginHost {
|
||||||
$params));
|
$params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shortcut syntax (disabled for now)
|
||||||
|
/* function get_method_url(Plugin $sender, string $method, $params) {
|
||||||
|
return get_self_url_prefix() . "/backend.php?" .
|
||||||
|
http_build_query(
|
||||||
|
array_merge(
|
||||||
|
[
|
||||||
|
"op" => strtolower(get_class($sender) . self::PUBLIC_METHOD_DELIMITER . $method),
|
||||||
|
],
|
||||||
|
$params));
|
||||||
|
} */
|
||||||
|
|
||||||
// WARNING: endpoint in public.php, exposed to unauthenticated users
|
// WARNING: endpoint in public.php, exposed to unauthenticated users
|
||||||
function get_public_method_url(Plugin $sender, string $method, $params) {
|
function get_public_method_url(Plugin $sender, string $method, $params) {
|
||||||
if ($sender->is_public_method($method)) {
|
if ($sender->is_public_method($method)) {
|
||||||
|
@ -618,7 +629,7 @@ class PluginHost {
|
||||||
http_build_query(
|
http_build_query(
|
||||||
array_merge(
|
array_merge(
|
||||||
[
|
[
|
||||||
"op" => strtolower(get_class($sender) . PluginHost::PUBLIC_METHOD_DELIMITER . $method),
|
"op" => strtolower(get_class($sender) . self::PUBLIC_METHOD_DELIMITER . $method),
|
||||||
],
|
],
|
||||||
$params));
|
$params));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11,6 +11,17 @@
|
||||||
return $rv;
|
return $rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shortcut syntax (disabled)
|
||||||
|
/* function pluginhandler_tags(\Plugin $plugin, string $method) {
|
||||||
|
return hidden_tag("op", strtolower(get_class($plugin) . \PluginHost::PUBLIC_METHOD_DELIMITER . $method));
|
||||||
|
} */
|
||||||
|
|
||||||
|
function pluginhandler_tags(\Plugin $plugin, string $method) {
|
||||||
|
return hidden_tag("op", "pluginhandler") .
|
||||||
|
hidden_tag("plugin", strtolower(get_class($plugin))) .
|
||||||
|
hidden_tag("method", $method);
|
||||||
|
}
|
||||||
|
|
||||||
function button_tag(string $value, string $type, array $attributes = []) {
|
function button_tag(string $value, string $type, array $attributes = []) {
|
||||||
return "<button dojoType=\"dijit.form.Button\" ".attributes_to_string($attributes)." type=\"$type\">".htmlspecialchars($value)."</button>";
|
return "<button dojoType=\"dijit.form.Button\" ".attributes_to_string($attributes)." type=\"$type\">".htmlspecialchars($value)."</button>";
|
||||||
}
|
}
|
||||||
|
@ -155,4 +166,3 @@
|
||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,9 @@ const App = {
|
||||||
|
|
||||||
return dijit.getEnclosingWidget(elem.closest('.dijitDialog'));
|
return dijit.getEnclosingWidget(elem.closest('.dijitDialog'));
|
||||||
},
|
},
|
||||||
|
getPhArgs(plugin, method, args = {}) {
|
||||||
|
return {...{op: "pluginhandler", plugin: plugin, method: method}, ...args};
|
||||||
|
},
|
||||||
label_to_feed_id: function(label) {
|
label_to_feed_id: function(label) {
|
||||||
return this.LABEL_BASE_INDEX - 1 - Math.abs(label);
|
return this.LABEL_BASE_INDEX - 1 - Math.abs(label);
|
||||||
},
|
},
|
||||||
|
|
|
@ -229,9 +229,7 @@ class Af_Proxy_Http extends Plugin {
|
||||||
}
|
}
|
||||||
</script>";
|
</script>";
|
||||||
|
|
||||||
print \Controls\hidden_tag("op", "pluginhandler");
|
print \Controls\pluginhandler_tags($this, "save");
|
||||||
print \Controls\hidden_tag("method", "save");
|
|
||||||
print \Controls\hidden_tag("plugin", "af_proxy_http");
|
|
||||||
|
|
||||||
$proxy_all = sql_bool_to_bool($this->host->get($this, "proxy_all"));
|
$proxy_all = sql_bool_to_bool($this->host->get($this, "proxy_all"));
|
||||||
print \Controls\checkbox_tag("proxy_all", $proxy_all);
|
print \Controls\checkbox_tag("proxy_all", $proxy_all);
|
||||||
|
|
|
@ -157,9 +157,7 @@ class Af_Psql_Trgm extends Plugin {
|
||||||
}
|
}
|
||||||
</script>";
|
</script>";
|
||||||
|
|
||||||
print \Controls\hidden_tag("op", "pluginhandler");
|
print \Controls\pluginhandler_tags($this, "save");
|
||||||
print \Controls\hidden_tag("method", "save");
|
|
||||||
print \Controls\hidden_tag("plugin", "af_psql_trgm");
|
|
||||||
|
|
||||||
print "<h2>" . __("Global settings") . "</h2>";
|
print "<h2>" . __("Global settings") . "</h2>";
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ Plugins.Af_Readability = {
|
||||||
|
|
||||||
Notify.progress("Loading, please wait...");
|
Notify.progress("Loading, please wait...");
|
||||||
|
|
||||||
xhrJson("backend.php",{ op: "pluginhandler", plugin: "af_readability", method: "embed", param: id }, (reply) => {
|
xhrJson("backend.php", App.getPhArgs("af_readability", "embed", {id: id}), (reply) => {
|
||||||
|
|
||||||
if (content && reply.content) {
|
if (content && reply.content) {
|
||||||
content.setAttribute(self.orig_attr_name, content.innerHTML);
|
content.setAttribute(self.orig_attr_name, content.innerHTML);
|
||||||
|
|
|
@ -67,9 +67,7 @@ class Af_Readability extends Plugin {
|
||||||
|
|
||||||
<form dojoType='dijit.form.Form'>
|
<form dojoType='dijit.form.Form'>
|
||||||
|
|
||||||
<?= \Controls\hidden_tag("op", "pluginhandler") ?>
|
<?= \Controls\pluginhandler_tags($this, "save") ?>
|
||||||
<?= \Controls\hidden_tag("method", "save") ?>
|
|
||||||
<?= \Controls\hidden_tag("plugin", "af_readability") ?>
|
|
||||||
|
|
||||||
<script type='dojo/method' event='onSubmit' args='evt'>
|
<script type='dojo/method' event='onSubmit' args='evt'>
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
@ -329,7 +327,7 @@ class Af_Readability extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
function embed() {
|
function embed() {
|
||||||
$article_id = (int) $_REQUEST["param"];
|
$article_id = (int) $_REQUEST["id"];
|
||||||
|
|
||||||
$sth = $this->pdo->prepare("SELECT link FROM ttrss_entries WHERE id = ?");
|
$sth = $this->pdo->prepare("SELECT link FROM ttrss_entries WHERE id = ?");
|
||||||
$sth->execute([$article_id]);
|
$sth->execute([$article_id]);
|
||||||
|
|
|
@ -41,9 +41,7 @@ class Af_RedditImgur extends Plugin {
|
||||||
|
|
||||||
<form dojoType='dijit.form.Form'>
|
<form dojoType='dijit.form.Form'>
|
||||||
|
|
||||||
<?= \Controls\hidden_tag("op", "pluginhandler") ?>
|
<?= \Controls\pluginhandler_tags($this, "save") ?>
|
||||||
<?= \Controls\hidden_tag("method", "save") ?>
|
|
||||||
<?= \Controls\hidden_tag("plugin", "af_redditimgur") ?>
|
|
||||||
|
|
||||||
<script type='dojo/method' event='onSubmit' args='evt'>
|
<script type='dojo/method' event='onSubmit' args='evt'>
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
@ -633,6 +631,10 @@ class Af_RedditImgur extends Plugin {
|
||||||
$entry->parentNode->insertBefore($img, $entry);*/
|
$entry->parentNode->insertBefore($img, $entry);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function csrf_ignore($method) {
|
||||||
|
return $method === "testurl";
|
||||||
|
}
|
||||||
|
|
||||||
function testurl() {
|
function testurl() {
|
||||||
|
|
||||||
$url = clean($_POST["url"]);
|
$url = clean($_POST["url"]);
|
||||||
|
@ -651,7 +653,6 @@ class Af_RedditImgur extends Plugin {
|
||||||
<input type="hidden" name="op" value="pluginhandler">
|
<input type="hidden" name="op" value="pluginhandler">
|
||||||
<input type="hidden" name="method" value="testurl">
|
<input type="hidden" name="method" value="testurl">
|
||||||
<input type="hidden" name="plugin" value="af_redditimgur">
|
<input type="hidden" name="plugin" value="af_redditimgur">
|
||||||
<input type="hidden" name="csrf_token" value="<?= $_SESSION["csrf_token"] ?>">
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label>URL:</label>
|
<label>URL:</label>
|
||||||
<input name="url" size="100" value="<?= htmlspecialchars($url) ?>"></input>
|
<input name="url" size="100" value="<?= htmlspecialchars($url) ?>"></input>
|
||||||
|
|
|
@ -45,9 +45,7 @@ class Mail extends Plugin {
|
||||||
title="<i class='material-icons'>mail</i> <?= __('Mail plugin') ?>">
|
title="<i class='material-icons'>mail</i> <?= __('Mail plugin') ?>">
|
||||||
|
|
||||||
<form dojoType="dijit.form.Form">
|
<form dojoType="dijit.form.Form">
|
||||||
<?= \Controls\hidden_tag("op", "pluginhandler") ?>
|
<?= \Controls\pluginhandler_tags($this, "save") ?>
|
||||||
<?= \Controls\hidden_tag("method", "save") ?>
|
|
||||||
<?= \Controls\hidden_tag("plugin", "mail") ?>
|
|
||||||
|
|
||||||
<script type="dojo/method" event="onSubmit" args="evt">
|
<script type="dojo/method" event="onSubmit" args="evt">
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
@ -150,12 +148,10 @@ class Mail extends Plugin {
|
||||||
|
|
||||||
<form dojoType='dijit.form.Form'>
|
<form dojoType='dijit.form.Form'>
|
||||||
|
|
||||||
<?= \Controls\hidden_tag("op", "pluginhandler") ?>
|
<?= \Controls\pluginhandler_tags($this, "sendemail") ?>
|
||||||
<?= \Controls\hidden_tag("plugin", "mail") ?>
|
|
||||||
<?= \Controls\hidden_tag("method", "sendEmail") ?>
|
|
||||||
|
|
||||||
<?= \Controls\hidden_tag("from_email", "$user_email") ?>
|
<?= \Controls\hidden_tag("from_email", $user_email) ?>
|
||||||
<?= \Controls\hidden_tag("from_name", "$user_name") ?>
|
<?= \Controls\hidden_tag("from_name", $user_name) ?>
|
||||||
|
|
||||||
<script type='dojo/method' event='onSubmit' args='evt'>
|
<script type='dojo/method' event='onSubmit' args='evt'>
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
|
@ -38,7 +38,7 @@ Plugins.Mail = {
|
||||||
const tmph = dojo.connect(dialog, 'onShow', function () {
|
const tmph = dojo.connect(dialog, 'onShow', function () {
|
||||||
dojo.disconnect(tmph);
|
dojo.disconnect(tmph);
|
||||||
|
|
||||||
xhrPost("backend.php", {op: "pluginhandler", plugin: "mail", method: "emailArticle", ids: id}, (transport) => {
|
xhrPost("backend.php", App.getPhArgs("mail", "emailArticle", {ids: id}), (transport) => {
|
||||||
dialog.attr('content', transport.responseText);
|
dialog.attr('content', transport.responseText);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,7 +21,7 @@ Plugins.Mailto = {
|
||||||
const tmph = dojo.connect(dialog, 'onShow', function () {
|
const tmph = dojo.connect(dialog, 'onShow', function () {
|
||||||
dojo.disconnect(tmph);
|
dojo.disconnect(tmph);
|
||||||
|
|
||||||
xhrPost("backend.php", {op: "pluginhandler", plugin: "mailto", method: "emailArticle", ids: id}, (transport) => {
|
xhrPost("backend.php", App.getPhArgs("mailto", "emailArticle", {ids: id}), (transport) => {
|
||||||
dialog.attr('content', transport.responseText);
|
dialog.attr('content', transport.responseText);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,9 +38,7 @@ class Note extends Plugin {
|
||||||
$note = $row['note'];
|
$note = $row['note'];
|
||||||
|
|
||||||
print \Controls\hidden_tag("id", $id);
|
print \Controls\hidden_tag("id", $id);
|
||||||
print \Controls\hidden_tag("op", "pluginhandler");
|
print \Controls\pluginhandler_tags($this, "setnote");
|
||||||
print \Controls\hidden_tag("method", "setNote");
|
|
||||||
print \Controls\hidden_tag("plugin", "note");
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<textarea dojoType='dijit.form.SimpleTextarea'
|
<textarea dojoType='dijit.form.SimpleTextarea'
|
||||||
|
|
|
@ -33,7 +33,7 @@ Plugins.Note = {
|
||||||
const tmph = dojo.connect(dialog, 'onShow', function () {
|
const tmph = dojo.connect(dialog, 'onShow', function () {
|
||||||
dojo.disconnect(tmph);
|
dojo.disconnect(tmph);
|
||||||
|
|
||||||
xhrPost("backend.php", {op: "pluginhandler", plugin: "note", method: "edit", id: id}, (transport) => {
|
xhrPost("backend.php", App.getPhArgs("note", "edit", {id: id}), (transport) => {
|
||||||
dialog.attr('content', transport.responseText);
|
dialog.attr('content', transport.responseText);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -50,9 +50,7 @@ class NSFW extends Plugin {
|
||||||
title="<i class='material-icons'>extension</i> <?= __("NSFW Plugin") ?>">
|
title="<i class='material-icons'>extension</i> <?= __("NSFW Plugin") ?>">
|
||||||
<form dojoType="dijit.form.Form">
|
<form dojoType="dijit.form.Form">
|
||||||
|
|
||||||
<?= \Controls\hidden_tag("op", "pluginhandler") ?>
|
<?= \Controls\pluginhandler_tags($this, "save") ?>
|
||||||
<?= \Controls\hidden_tag("method", "save") ?>
|
|
||||||
<?= \Controls\hidden_tag("plugin", "nsfw") ?>
|
|
||||||
|
|
||||||
<script type="dojo/method" event="onSubmit" args="evt">
|
<script type="dojo/method" event="onSubmit" args="evt">
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
|
@ -10,9 +10,7 @@ Plugins.Share = {
|
||||||
|
|
||||||
Notify.progress("Trying to change URL...", true);
|
Notify.progress("Trying to change URL...", true);
|
||||||
|
|
||||||
const query = {op: "pluginhandler", plugin: "share", method: "newkey", id: id};
|
xhrJson("backend.php", App.getPhArgs("share", "newkey", {id: id}), (reply) => {
|
||||||
|
|
||||||
xhrJson("backend.php", query, (reply) => {
|
|
||||||
if (reply) {
|
if (reply) {
|
||||||
const new_link = reply.link;
|
const new_link = reply.link;
|
||||||
const target = dialog.domNode.querySelector(".target-url");
|
const target = dialog.domNode.querySelector(".target-url");
|
||||||
|
@ -45,7 +43,7 @@ Plugins.Share = {
|
||||||
},
|
},
|
||||||
unshare: function () {
|
unshare: function () {
|
||||||
if (confirm(__("Remove sharing for this article?"))) {
|
if (confirm(__("Remove sharing for this article?"))) {
|
||||||
xhrPost("backend.php", {op: "pluginhandler", plugin: "share", method: "unshare", id: id}, (transport) => {
|
xhrPost("backend.php", App.getPhArgs("share", "unshare", {id: id}), (transport) => {
|
||||||
Notify.info(transport.responseText);
|
Notify.info(transport.responseText);
|
||||||
|
|
||||||
const icon = document.querySelector(".share-icon-" + id);
|
const icon = document.querySelector(".share-icon-" + id);
|
||||||
|
@ -64,7 +62,7 @@ Plugins.Share = {
|
||||||
const tmph = dojo.connect(dialog, 'onShow', function () {
|
const tmph = dojo.connect(dialog, 'onShow', function () {
|
||||||
dojo.disconnect(tmph);
|
dojo.disconnect(tmph);
|
||||||
|
|
||||||
xhrPost("backend.php", {op: "pluginhandler", plugin: "share", method: "shareDialog", id: id}, (transport) => {
|
xhrPost("backend.php", App.getPhArgs("share", "shareDialog", {id: id}), (transport) => {
|
||||||
dialog.attr('content', transport.responseText)
|
dialog.attr('content', transport.responseText)
|
||||||
|
|
||||||
const icon = document.querySelector(".share-icon-" + id);
|
const icon = document.querySelector(".share-icon-" + id);
|
||||||
|
|
|
@ -5,7 +5,7 @@ Plugins.Share = {
|
||||||
if (confirm(__("This will invalidate all previously shared article URLs. Continue?"))) {
|
if (confirm(__("This will invalidate all previously shared article URLs. Continue?"))) {
|
||||||
Notify.progress("Clearing URLs...");
|
Notify.progress("Clearing URLs...");
|
||||||
|
|
||||||
xhrPost("backend.php", {op: "pluginhandler", plugin: "share", method: "clearArticleKeys"}, (transport) => {
|
xhrPost("backend.php", App.getPhArgs("share", "clearArticleKeys"), (transport) => {
|
||||||
Notify.info(transport.responseText);
|
Notify.info(transport.responseText);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue