requestPermission: improve UX & docs (#312)

* requestPermission: change Ignore button to checkbox

* Update requestPermission documentation

- describe the possible results
- document ignoreOriginList
This commit is contained in:
introt 2022-04-14 04:00:52 +00:00 committed by Alex Yatskov
parent 8e9879f80f
commit 81fccbfac6
2 changed files with 58 additions and 48 deletions

View File

@ -1455,11 +1455,18 @@ corresponding to when the API was available for use.
* **requestPermission**
Request permission to use the API exposed by this plugin. Only request coming from origin listed in the
`webCorsOriginList` option are allowed to use the Api. Calling this method will display a popup asking the user
if he want to allow your origin to use the Api. This is the only method that can be called even if the origin of
the request isn't in the `webCorsOriginList` list. It also doesn't require the api key. Calling this method will
not display the popup if the origin is already trusted.
Requests permission to use the API exposed by this plugin. This method does not require the API key, and is the
only one that accepts requests from any origin; the other methods only accept requests from trusted origins,
which are listed under `webCorsOriginList` in the add-on config. `localhost` is trusted by default.
Calling this method from an untrusted origin will display a popup in Anki asking the user whether they want to
allow your origin to use the API; calls from trusted origins will return the result without displaying the popup.
When denying permission, the user may also choose to ignore further permission requests from that origin. These
origins end up in the `ignoreOriginList`, editable via the add-on config.
The result always contains the `permission` field, which in turn contains either the string `granted` or `denied`,
corresponding to whether your origin is trusted. If your origin is trusted, the fields `requireApiKey` (`true` if
required) and `version` will also be returned.
This should be the first call you make to make sure that your application and Anki-Connect are able to communicate
properly with each other. New versions of Anki-Connect are backwards compatible; as long as you are using actions

View File

@ -28,7 +28,7 @@ import unicodedata
from PyQt5 import QtCore
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtWidgets import QMessageBox, QCheckBox
import anki
import anki.exporting
@ -364,53 +364,56 @@ class AnkiConnect:
def version(self):
return util.setting('apiVersion')
@util.api()
def requestPermission(self, origin, allowed):
results = {
"permission": "denied",
}
if allowed:
return {
"permission": "granted",
"requireApikey": bool(util.setting('apiKey')),
"version": util.setting('apiVersion')
}
if origin in util.setting('ignoreOriginList') :
return {
"permission": "denied",
}
msg = QMessageBox(None)
msg.setWindowTitle("A website want to access to Anki")
msg.setText(origin + " request permission to use Anki through AnkiConnect.\nDo you want to give it access ?")
msg.setInformativeText("By giving permission, the website will be able to do actions on anki, including destructives actions like deck deletion.")
msg.setWindowIcon(self.window().windowIcon())
msg.setIcon(QMessageBox.Question)
msg.setStandardButtons(QMessageBox.Yes|QMessageBox.Ignore|QMessageBox.No)
msg.setDefaultButton(QMessageBox.No)
msg.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
pressedButton = msg.exec_()
if pressedButton == QMessageBox.Yes:
config = aqt.mw.addonManager.getConfig(__name__)
config["webCorsOriginList"] = util.setting('webCorsOriginList')
config["webCorsOriginList"].append(origin)
aqt.mw.addonManager.writeConfig(__name__, config)
if pressedButton == QMessageBox.Ignore:
config = aqt.mw.addonManager.getConfig(__name__)
config["ignoreOriginList"] = util.setting('ignoreOriginList')
config["ignoreOriginList"].append(origin)
aqt.mw.addonManager.writeConfig(__name__, config)
if pressedButton == QMessageBox.Yes:
results = {
"permission": "granted",
"requireApikey": bool(util.setting('apiKey')),
"version": util.setting('apiVersion')
}
else :
results = {
"permission": "denied",
"permission": "granted",
"requireApikey": bool(util.setting('apiKey')),
"version": util.setting('apiVersion')
}
elif origin in util.setting('ignoreOriginList'):
pass # defaults to denied
else: # prompt the user
msg = QMessageBox(None)
msg.setWindowTitle("A website wants to access to Anki")
msg.setText('"{}" requests permission to use Anki through AnkiConnect. Do you want to give it access?'.format(origin))
msg.setInformativeText("By granting permission, you'll allow the website to modify your collection on your behalf, including the execution of destructive actions such as deck deletion.")
msg.setWindowIcon(self.window().windowIcon())
msg.setIcon(QMessageBox.Question)
msg.setStandardButtons(QMessageBox.Yes|QMessageBox.No)
msg.setDefaultButton(QMessageBox.No)
msg.setCheckBox(QCheckBox(text='Ignore further requests from "{}"'.format(origin), parent=msg))
msg.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
pressedButton = msg.exec_()
if pressedButton == QMessageBox.Yes:
config = aqt.mw.addonManager.getConfig(__name__)
config["webCorsOriginList"] = util.setting('webCorsOriginList')
config["webCorsOriginList"].append(origin)
aqt.mw.addonManager.writeConfig(__name__, config)
results = {
"permission": "granted",
"requireApikey": bool(util.setting('apiKey')),
"version": util.setting('apiVersion')
}
# if the origin isn't an empty string, the user clicks "No", and the ignore box is checked
elif origin and pressedButton == QMessageBox.No and msg.checkBox().isChecked():
config = aqt.mw.addonManager.getConfig(__name__)
config["ignoreOriginList"] = util.setting('ignoreOriginList')
config["ignoreOriginList"].append(origin)
aqt.mw.addonManager.writeConfig(__name__, config)
# else defaults to denied
return results