You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/extensions.md
+95-16Lines changed: 95 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
A developer's guide for the Specter Desktop `Extension` framework.
4
4
5
-
We currently rework the naming of extensions/plugins/services. If not otherwise stated, you can see those three terms as the same, for now.
5
+
We currently rework the naming of extensions/plugins/services. If not otherwise stated, for now, they are used interchangeably.
6
6
7
7
## TL;DR
8
8
@@ -104,7 +104,7 @@ All the attributes of an extension are currently (json support is planned) defin
104
104
105
105
## Extension attributes
106
106
Here is an example. This class definition MUST be stored in a file called "service.py" within a package with the name `org-id.specterext.extions-id`.
107
-
```
107
+
```python
108
108
classDiceService(Service):
109
109
id="dice"
110
110
name ="Specter Dice"
@@ -117,18 +117,18 @@ class DiceService(Service):
117
117
devstatus = devstatus_alpha
118
118
```
119
119
This defines the base `Service` class (to be renamed to "Extension") that all extensions must inherit from. This also enables `extension` auto-discovery. Any feature that is common to most or all `Service` integrations should be implemented here.
120
-
With inheriting from `Service` you get some usefull methods explained later.
120
+
With inheriting from `Service` you get some useful methods explained later.
121
121
122
-
The `id` needs to be unique within a specific specter-instance where this extension is part of. The `name` is the displayname as shown to the user in the plugin-area (currently there is not yet a technical difference between extensions and plugins). The `icon` will be used where labels are used to be diplayed if this extension is reserving addresses. The `logo` and the `desc`ription is also used in the plugin-area ("choose plugins").
122
+
The `id` needs to be unique within a specific Specter instance where this extension is part of. The `name` is the display name as shown to the user in the pluginarea (currently there is not yet a technical difference between extensions and plugins). The `icon` will be used where labels are used to be diplayed if this extension is reserving addresses. The `logo` and the `desc`ription is also used in the pluginarea ("Choose plugins").
123
123
If the extension has a UI (currently all of them have one), `has_blueprint` is True. `The blueprint_module` is referencing the controller module where endpoints are defined. It's recommended to follow the format `org.specterext.extions-id.controller`.
124
124
`isolated_client` should not be used yet. It is determining where in the url-path tree the blueprint will be mounted. This might have an impact on whether the extension's frontend client has access to the cookie used in Specter. Check `config.py` for details.
125
-
`devstatus` is one of `devstatus_alpha`, `devstatus_beta` or `devstatus_prod` defined in `cryptoadvance.specter.services.service`. Each Specter instance will have a config variable called `SERVICES_DEVSTATUS_THRESHOLD` (prod in Production and alpha in Development) and depending on that, the plugin will be available to the user.
125
+
`devstatus` is one of `devstatus_alpha`, `devstatus_beta` or `devstatus_prod` defined in `cryptoadvance.specter.services.service`. Each Specter instance will have a config variable called `SERVICES_DEVSTATUS_THRESHOLD` (prod in Production and alpha in Development) and depending on that, the plugin will be available to the user.
126
126
127
127
## Frontend aspects
128
128
129
129
As stated, you can have your own frontend with a blueprint. If you only have one, it needs to have a `/` route in order to be linkable from the `choose your plugin` page.
130
130
If you create your extension with a blueprint, it'll also create a controller for you which, simplified, looks like this:
131
-
```
131
+
```python
132
132
rubberduck_endpoint = ScratchpadService.blueprint
133
133
134
134
defext() -> ScratchpadService:
@@ -150,20 +150,20 @@ def index():
150
150
[...]
151
151
```
152
152
But you can also have more than one blueprint. Define them like this in your service class:
You have to have a default blueprint which has the above mentioned index page.
160
160
In your controller, the endpoint needs to be specified like this:
161
-
```
161
+
```python
162
162
ui = RubberduckService.blueprints["ui"]
163
163
```
164
164
165
-
You might have an extension which wants to inject e.g. javascript code into each and every page of specter-desktop. The extension needs to be activated for the user, though. You can do that via overwriting one of the `inject_in_basejinja_*` methods in your service-class:
166
-
```
165
+
You might have an extension which wants to inject e.g. JavaScript code into each and every page of Specter Desktop. The extension needs to be activated for the user, though. You can do that via overwriting one of the `inject_in_basejinja_*` methods in your serviceclass:
166
+
```python
167
167
@classmethod
168
168
definject_in_basejinja_head(cls):
169
169
''' e.g. rendering some snippet '''
@@ -212,14 +212,14 @@ This is also where `Service`-wide configuration or other information should be s
212
212
Because the `ServiceEncryptedStorage` is specific to each individual user, this manager provides convenient access to automatically retrieve the `current_user` from the Flask context and provide the correct user's `ServiceEncryptedStorage`. It is implemented as a `Singleton` which can be retrieved simply by importing the class and calling `get_instance()`.
213
213
214
214
This simplifies code to just asking for:
215
-
```
215
+
```python
216
216
from .service_encrypted_storage import ServiceEncryptedStorageManager
@@ -248,19 +248,98 @@ Unfortunately, the two unencrypted classes are derived from the encrypted one ra
248
248
249
249
### Service configuration
250
250
In order to separate the service-configuration from the main-configuration, you can specify your config in a file called `config.py`. It's structure is similiar to the specter-wide `config.py`, e.g.:
251
-
```
251
+
```python
252
252
classBaseConfig():
253
253
SWAN_API_URL="https://dev-api.swanbitcoin.com"
254
254
255
255
classProductionConfig(BaseConfig):
256
256
SWAN_API_URL="https://api.swanbitcoin.com"
257
257
```
258
258
In your code, you can access the correct value as in any other flask-code, like `api_url = app.config.get("SWAN_API_URL")`. If the instance is running a config (e.g. `DevelopmentConfig`) which is not available in your service-specific config (as above), the inheritance-hirarchy from the mainconfig will get traversed and the first hit will get get configured. In this example, it would be `BaseConfig`.
259
+
259
260
### Callback methods
260
-
Your service class will inherit a callback-method which will get called for various reasons with the "reason" being a string as the first parameter. Checkout the `cryptoadvance.specter.services.callbacks` file for the specific callbacks.
261
+
Your service class will inherit a callbackmethod which will get called for various reasons with the "reason" being a string as the first parameter. Checkout the `cryptoadvance.specter.services.callbacks` file for the specific callbacks.
261
262
262
-
Some important one is the `after_serverpy_init_app` which passes a `Scheduler` class which can be used to setup regular tasks. A list of currently implemented callback-methods along with their descriptions are available in [`/src/cryptoadvance/specter/services/callbacks.py`](https://github.com/cryptoadvance/specter-desktop/blob/master/src/cryptoadvance/specter/services/callbacks.py).
263
+
Some important one is the `after_serverpy_init_app` which passes a `Scheduler` class which can be used to setup regular tasks. A list of currently implemented callbackmethods along with their descriptions are available in [`/src/cryptoadvance/specter/services/callbacks.py`](https://github.com/cryptoadvance/specter-desktop/blob/master/src/cryptoadvance/specter/services/callbacks.py).
263
264
264
265
### `controller.py`
265
-
The minimal url routes for `Service` selection and management. As usualy in Flask, `templates` and `static` resources are in their respective subfolders. Please note that there is an additional directory with the id of the extension which looks redundant at first. This is due to the way blueprints are loading templates and ensures that there are no naming collisions. Maybe at a later stage, this can be used to let plugins override other plugin's templates.
266
+
The minimal url routes for `Service` selection and management. As usual in Flask, `templates` and `static` resources are in their respective subfolders. Please note that there is an additional directory with the id of the extension which looks redundant at first. This is due to the way blueprints are loading templates and ensures that there are no naming collisions. Maybe at a later stage, this can be used to let plugins override other plugin's templates.
267
+
268
+
### Extending the settings dialog
269
+
You can extend the settings dialog with your own templates. To do that, create a callback method in your service like:
270
+
```python
271
+
from cryptoadvance.specter.services import callbacks
272
+
# [...]
273
+
defcallback_add_settingstabs(self):
274
+
''' Extending the settings tab with an own tab called "myexttitle" '''
In this case, this would add a tab called "myexttitle" and you're now supposed to provide an endpoint in your controller which might be called `myext_something` e.g. like this:
0 commit comments