diff --git a/requirements.in b/requirements.in index 11f8c0087..999a6fafc 100644 --- a/requirements.in +++ b/requirements.in @@ -2,7 +2,7 @@ certifi==2022.12.7 chardet==3.0.4 Click==8.1.1 Flask==2.1.1 -Werkzeug==2.0.0 # need to pin because Flask doesn't work with newer ones +Werkzeug==2.0.3 # need to pin because Flask doesn't work with newer ones Flask-Babel==2.0.0 Flask-Cors==3.0.10 Flask-Login==0.5.0 @@ -25,6 +25,7 @@ mnemonic==0.20 cryptography==3.4.7 Flask-APScheduler==1.12.4 gunicorn==20.1.0 +simple-websocket==0.8.1 protobuf==3.20.2 PyJWT==2.4.0 pytimeparse==1.1.8 @@ -34,4 +35,4 @@ cryptoadvance-liquidissuer==0.2.4 specterext-exfund==0.1.7 specterext-faucet==0.1.2 cryptoadvance.spectrum==0.3.1 -specterext-stacktrack==0.2.1 \ No newline at end of file +specterext-stacktrack==0.2.1 diff --git a/requirements.txt b/requirements.txt index 7e0339e70..f2554eb74 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile +# This file is autogenerated by pip-compile with python 3.10 # To update, run: # # pip-compile --generate-hashes requirements.in @@ -12,9 +12,9 @@ apscheduler==3.9.1 \ --hash=sha256:65e6574b6395498d371d045f2a8a7e4f7d50c6ad21ef7313d15b1c7cf20df1e3 \ --hash=sha256:ddc25a0ddd899de44d7f451f4375fb971887e65af51e41e5dcf681f59b8b2c9a # via flask-apscheduler -babel==2.10.3 \ - --hash=sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51 \ - --hash=sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb +babel==2.11.0 \ + --hash=sha256:1ad3eca1c885218f6dce2ab67291178944f810a10a9b5f3cb8382a5a232b64fe \ + --hash=sha256:5ef4b3226b0180dedded4229651c8b0e1a3a6a2837d45a073272f313e4cf97f6 # via flask-babel base58==2.1.1 \ --hash=sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2 \ @@ -119,7 +119,7 @@ cryptoadvance-liquidissuer==0.2.4 \ --hash=sha256:5a2c531801854c5a4a46daf184877e22f731cdb42d2cfb840785bda7371ba6fb \ --hash=sha256:9e468f3e35ecc566b3f74a2263677cf26632548abb194521dba15ad37acd1e9b # via -r requirements.in -cryptoadvance.spectrum==0.3.1 \ +cryptoadvance-spectrum==0.3.1 \ --hash=sha256:c0eedce7d88f28a1fbf3a5afbcf3b03f1effdb0983f07445953a1d9fbe789209 \ --hash=sha256:c8eba63d3aec6cc391784e5dde99ec39b13f719a062ef1ce786e5fdf850150d8 # via -r requirements.in @@ -153,7 +153,21 @@ embit==0.6.1 \ --hash=sha256:16a84c6668dc9ffc907594457a46f7142cee379646bc009a5a9b77b0d2cb4e12 # via # -r requirements.in - # cryptoadvance.spectrum + # cryptoadvance-spectrum +flask==2.1.1 \ + --hash=sha256:8a4cf32d904cf5621db9f0c9fbcd7efabf3003f22a04e4d0ce790c7137ec5264 \ + --hash=sha256:a8c9bd3e558ec99646d177a9739c41df1ded0629480b4c8d2975412f3c9519c8 + # via + # -r requirements.in + # cryptoadvance-spectrum + # flask-apscheduler + # flask-babel + # flask-cors + # flask-httpauth + # flask-login + # flask-restful + # flask-sqlalchemy + # flask-wtf flask-apscheduler==1.12.4 \ --hash=sha256:681dae34dc6cc9403ce674795e53abd0bff540472129cfd3d3c93e0e1d502da8 # via -r requirements.in @@ -180,22 +194,8 @@ flask-restful==0.3.9 \ flask-sqlalchemy==2.5.1 \ --hash=sha256:2bda44b43e7cacb15d4e05ff3cc1f8bc97936cc464623424102bfc2c35e95912 \ --hash=sha256:f12c3d4cc5cc7fdcc148b9527ea05671718c3ea45d50c7e732cceb33f574b390 - # via cryptoadvance.spectrum -flask==2.1.1 \ - --hash=sha256:8a4cf32d904cf5621db9f0c9fbcd7efabf3003f22a04e4d0ce790c7137ec5264 \ - --hash=sha256:a8c9bd3e558ec99646d177a9739c41df1ded0629480b4c8d2975412f3c9519c8 - # via - # -r requirements.in - # cryptoadvance.spectrum - # flask-apscheduler - # flask-babel - # flask-cors - # flask-httpauth - # flask-login - # flask-restful - # flask-sqlalchemy - # flask-wtf -flask_wtf==0.15.1 \ + # via cryptoadvance-spectrum +flask-wtf==0.15.1 \ --hash=sha256:6ff7af73458f182180906a37a783e290bdc8a3817fe4ad17227563137ca285bf \ --hash=sha256:ff177185f891302dc253437fe63081e7a46a4e99aca61dfe086fb23e54fff2dc # via -r requirements.in @@ -271,6 +271,10 @@ gunicorn==20.1.0 \ --hash=sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e \ --hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8 # via -r requirements.in +h11==0.14.0 \ + --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ + --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 + # via wsproto hidapi==0.12.0.post2 \ --hash=sha256:00bad74617622b7b7abbb24eae5c11bd91957bb87217d62d8feea15c971a77b5 \ --hash=sha256:0292a29325b905fbfbb39cc368055cda63339fed684eceb7d4d241acc5e60d50 \ @@ -317,7 +321,7 @@ idna==3.4 \ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 # via requests -importlib_metadata==4.8.1 \ +importlib-metadata==4.8.1 \ --hash=sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15 \ --hash=sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1 # via -r requirements.in @@ -594,7 +598,7 @@ psycopg2-binary==2.9.5 \ --hash=sha256:f95b8aca2703d6a30249f83f4fe6a9abf2e627aa892a5caaab2267d56be7ab69 # via # -r requirements.in - # cryptoadvance.spectrum + # cryptoadvance-spectrum pyaes==1.6.1 \ --hash=sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f # via hwi @@ -637,29 +641,33 @@ pytimeparse==1.1.8 \ --hash=sha256:04b7be6cc8bd9f5647a6325444926c3ac34ee6bc7e69da4367ba282f076036bd \ --hash=sha256:e86136477be924d7e670646a98561957e8ca7308d44841e21f5ddea757556a0a # via -r requirements.in -pytz-deprecation-shim==0.1.0.post0 \ - --hash=sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6 \ - --hash=sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d - # via tzlocal -pytz==2022.5 \ - --hash=sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22 \ - --hash=sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914 +pytz==2022.7.1 \ + --hash=sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0 \ + --hash=sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a # via # apscheduler # babel # flask-babel # flask-restful # pandas +pytz-deprecation-shim==0.1.0.post0 \ + --hash=sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6 \ + --hash=sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d + # via tzlocal requests==2.26.0 \ --hash=sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24 \ --hash=sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7 # via # -r requirements.in - # cryptoadvance.spectrum + # cryptoadvance-spectrum semver==2.13.0 \ --hash=sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4 \ --hash=sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f # via bitbox02 +simple-websocket==0.8.1 \ + --hash=sha256:0abe874f6a0c6ddd197dbd4f0ce5708b47610a0d14fa2b0762e515659573d44a \ + --hash=sha256:bab2f34151d8b9abb1ea6d911e5edc8232fc203b6f814d179db0ba3f93e4e026 + # via -r requirements.in six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 @@ -751,12 +759,16 @@ urllib3==1.26.12 \ --hash=sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e \ --hash=sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997 # via requests -werkzeug==2.0.0 \ - --hash=sha256:3389bbfe6d40c6dd25e6d3f974155163c8b3de5bbda6a89342d4ab93fae80ba0 \ - --hash=sha256:64c02f6495ba01eddd6625b3675f357cd358a73f1e38458a56ad86c5baa30b53 +werkzeug==2.0.3 \ + --hash=sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8 \ + --hash=sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c # via # -r requirements.in # flask +wsproto==1.2.0 \ + --hash=sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065 \ + --hash=sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736 + # via simple-websocket wtforms==3.0.1 \ --hash=sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc \ --hash=sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b diff --git a/src/cryptoadvance/specter/cli/cli_server.py b/src/cryptoadvance/specter/cli/cli_server.py index 9a4947399..aba1b2469 100644 --- a/src/cryptoadvance/specter/cli/cli_server.py +++ b/src/cryptoadvance/specter/cli/cli_server.py @@ -125,6 +125,29 @@ def server( if key: app.config["KEY"] = key + # the app.config needs to be configured before init_app, such that the service callbacks + # like after_serverpy_init_app have this information available + if host != app.config["HOST"]: + app.config["HOST"] = host + + # set up kwargs dict for app.run + kwargs = { + "host": host, + "port": app.config["PORT"], + } + # watch templates folder to reload when something changes + extra_dirs = ["templates"] + extra_files = extra_dirs[:] + for extra_dir in extra_dirs: + for dirname, dirs, files in os.walk(extra_dir): + for filename in files: + filename = os.path.join(dirname, filename) + if os.path.isfile(filename): + extra_files.append(filename) + kwargs["extra_files"] = extra_files + + kwargs = configure_ssl(kwargs, app.config, ssl) + app.app_context().push() init_app(app, hwibridge=hwibridge) @@ -140,19 +163,6 @@ def server( toraddr_file = path.join(app.specter.data_folder, "onion.txt") - # watch templates folder to reload when something changes - extra_dirs = ["templates"] - extra_files = extra_dirs[:] - for extra_dir in extra_dirs: - for dirname, dirs, files in os.walk(extra_dir): - for filename in files: - filename = os.path.join(dirname, filename) - if os.path.isfile(filename): - extra_files.append(filename) - - kwargs = {"host": host, "port": app.config["PORT"], "extra_files": extra_files} - kwargs = configure_ssl(kwargs, app.config, ssl) - if hwibridge: if kwargs.get("ssl_context"): logger.error( diff --git a/src/cryptoadvance/specter/config.py b/src/cryptoadvance/specter/config.py index 8ab5b1bbf..b93d2d1d9 100644 --- a/src/cryptoadvance/specter/config.py +++ b/src/cryptoadvance/specter/config.py @@ -57,6 +57,7 @@ class BaseConfig(object): # The prefix for extensions which don't get access to the session cookie (if SPECTER_URL_PREFIX isn't compromised) ISOLATED_CLIENT_EXT_URL_PREFIX = "/ext" + HOST = os.getenv("HOST", "127.0.0.1") PORT = os.getenv("PORT", 25441) CONNECT_TOR = _get_bool_env_var(os.getenv("CONNECT_TOR", "False")) SPECTER_DATA_FOLDER = os.path.expanduser( @@ -183,6 +184,7 @@ class BaseConfig(object): "cryptoadvance.specterext.swan.service", "cryptoadvance.specterext.liquidissuer.service", "cryptoadvance.specterext.devhelp.service", + "cryptoadvance.specterext.notifications.service", "cryptoadvance.specterext.exfund.service", "cryptoadvance.specterext.faucet.service", "cryptoadvance.specterext.electrum.service", diff --git a/src/cryptoadvance/specter/managers/service_manager/service_manager.py b/src/cryptoadvance/specter/managers/service_manager/service_manager.py index 7798a1955..cc17e817e 100644 --- a/src/cryptoadvance/specter/managers/service_manager/service_manager.py +++ b/src/cryptoadvance/specter/managers/service_manager/service_manager.py @@ -19,7 +19,7 @@ from flask.blueprints import Blueprint from cryptoadvance.specter.util.specter_migrator import SpecterMigration -from ...services.service import Service +from ...services.service import Service, ServiceOptionality from ...services import callbacks, ExtensionException from ...util.reflection import ( _get_module_from_class, @@ -395,6 +395,16 @@ class ServiceManager: self.specter.service_unencrypted_storage_manager.delete_all_service_data(user) logger.debug(f"Deleted unencrypted services") + def add_required_services_to_users(self, users, force_opt_out=False): + "Adds the mandatory and opt_out (only if no services activated for user) services to users" + for service in self.services.values(): + for user in users: + if service.optionality == ServiceOptionality.mandatory or ( + service.optionality == ServiceOptionality.opt_out + and ((service.id not in user.services) or force_opt_out) + ): + user.add_service(service.id) + @classmethod def get_service_x_dirs(cls, x): """returns a list of package-directories which each represents a specific service. diff --git a/src/cryptoadvance/specter/server.py b/src/cryptoadvance/specter/server.py index c2c6dec49..b1d880085 100644 --- a/src/cryptoadvance/specter/server.py +++ b/src/cryptoadvance/specter/server.py @@ -162,6 +162,13 @@ def init_app(app: SpecterFlask, hwibridge=False, specter=None): specter=specter, devstatus_threshold=app.config["SERVICES_DEVSTATUS_THRESHOLD"] ) + def service_manager_cleanup_on_exit(signum, frame): + return specter.service_manager.execute_ext_callbacks( + callbacks.cleanup_on_exit, signum, frame + ) + + specter.call_functions_at_cleanup_on_exit.append(service_manager_cleanup_on_exit) + specter.initialize() # HWI @@ -206,9 +213,17 @@ def init_app(app: SpecterFlask, hwibridge=False, specter=None): from cryptoadvance.specter.server_endpoints import controller from cryptoadvance.specter.services import controller as serviceController + # this number of view_functions needs to be updated by hand when some are added or removed. + number_of_expected_view_functions = 105 if app.config.get("TESTING"): - logger.info(f"We have {len(app.view_functions)} view Functions") - if app.config.get("TESTING") and len(app.view_functions) <= 51: + logger.info( + f"We have {len(app.view_functions)} view Functions. " + f"There should be {number_of_expected_view_functions}." + ) + if ( + app.config.get("TESTING") + and len(app.view_functions) < number_of_expected_view_functions + ): # Need to force a reload as otherwise the import is skipped # in pytest, the app is created anew for each test # But we shouldn't do that if not necessary as this would result in @@ -277,6 +292,7 @@ def init_app(app: SpecterFlask, hwibridge=False, specter=None): scheduler.init_app(app) scheduler.start() + specter.service_manager.add_required_services_to_users(specter.user_manager.users) logger.info("----> starting service callback_after_serverpy_init_app ") specter.service_manager.execute_ext_callbacks( after_serverpy_init_app, scheduler=scheduler diff --git a/src/cryptoadvance/specter/server_endpoints/__init__.py b/src/cryptoadvance/specter/server_endpoints/__init__.py index cf00765ad..d354a54a5 100644 --- a/src/cryptoadvance/specter/server_endpoints/__init__.py +++ b/src/cryptoadvance/specter/server_endpoints/__init__.py @@ -1,4 +1,6 @@ from flask import flash as flask_flash +from flask import current_app as app +from ..services import callbacks def flash(*args, **kwargs): @@ -6,4 +8,11 @@ def flash(*args, **kwargs): This function could be placed in util but as it might use the service_manager, we place it here for now. """ - flask_flash(*args, **kwargs) + + return_values = app.specter.service_manager.execute_ext_callbacks( + callbacks.flash, *args, **kwargs + ) + + # if no extension handled the callback + if not return_values: + flask_flash(*args, **kwargs) diff --git a/src/cryptoadvance/specter/server_endpoints/auth.py b/src/cryptoadvance/specter/server_endpoints/auth.py index 40b02ddd1..022abe1e9 100644 --- a/src/cryptoadvance/specter/server_endpoints/auth.py +++ b/src/cryptoadvance/specter/server_endpoints/auth.py @@ -182,6 +182,7 @@ def register(): plaintext_password=password, config=config, ) + app.specter.service_manager.add_required_services_to_users([user]) flash( _( diff --git a/src/cryptoadvance/specter/services/callbacks.py b/src/cryptoadvance/specter/services/callbacks.py index 027f24bf2..fe7de24c9 100644 --- a/src/cryptoadvance/specter/services/callbacks.py +++ b/src/cryptoadvance/specter/services/callbacks.py @@ -57,3 +57,19 @@ flask_before_request = "flask_before_request" Will get called right after having access to app.specter """ specter_added_to_flask_app = "specter_added_to_flask_app" + +""" + Will get called when the server_endpoints.flash is called +""" +flash = "flash" + +""" + Callback that is not used yet, but could be implmented in server_endpoints just as flash +""" +create_and_show_notification = "create_and_show_notification" + + +""" + Callback that is called last in specter.cleanup_on_exit() +""" +cleanup_on_exit = "cleanup_on_exit" diff --git a/src/cryptoadvance/specter/services/service.py b/src/cryptoadvance/specter/services/service.py index f81340306..811f724b1 100644 --- a/src/cryptoadvance/specter/services/service.py +++ b/src/cryptoadvance/specter/services/service.py @@ -25,6 +25,12 @@ devstatus_beta = "beta" devstatus_prod = "prod" +class ServiceOptionality: + mandatory = "mandatory" # mandatory is not used yet by any service. Before it can be used it has to be clarified what happens in delete_services_with_encrypted_storage of mandatory services + opt_in = "opt_in" + opt_out = "opt_out" + + class Service: """A base class for Services""" @@ -38,6 +44,8 @@ class Service: # If the blueprint gets a "/ext" prefix (isolated_client = True), the login cookie won't work for all specter core functionality isolated_client = True devstatus = devstatus_alpha + optionality = ServiceOptionality.opt_in + visible_in_sidebar = True encrypt_data = False def __init__(self, active, specter): diff --git a/src/cryptoadvance/specter/specter.py b/src/cryptoadvance/specter/specter.py index e907a9fd4..43ec69e4e 100644 --- a/src/cryptoadvance/specter/specter.py +++ b/src/cryptoadvance/specter/specter.py @@ -40,6 +40,7 @@ from .persistence import read_json_file, write_json_file, write_node from .process_controller.bitcoind_controller import BitcoindPlainController from .rpc import BitcoinRPC, RpcError, get_default_datadir from .services.service import devstatus_alpha, devstatus_beta, devstatus_prod +from .services import callbacks from .specter_error import ExtProcTimeoutException, SpecterError from .tor_daemon import TorDaemonController from .user import User @@ -79,6 +80,7 @@ class Specter: if not os.path.isdir(data_folder): os.makedirs(data_folder) + self.call_functions_at_cleanup_on_exit = [] self.data_folder = data_folder self._config = config self._internal_bitcoind_version = internal_bitcoind_version @@ -187,6 +189,7 @@ class Specter: signal.signal(signal.SIGINT, self.cleanup_on_exit) # This is for kill $pid --> SIGTERM signal.signal(signal.SIGTERM, self.cleanup_on_exit) + # a list of functions that are called at cleanup_on_exit taking in each signum, frame def cleanup_on_exit(self, signum=0, frame=0): if self._tor_daemon: @@ -197,6 +200,9 @@ class Specter: if not node.external_node: node.stop() + for f in self.call_functions_at_cleanup_on_exit: + f(signum, frame) + logger.info("Closing Specter after cleanup") # For some reason we need to explicitely exit here. Otherwise it will hang exit(0) diff --git a/src/cryptoadvance/specter/static/styles.css b/src/cryptoadvance/specter/static/styles.css index 299c4531a..f5e3b267a 100644 --- a/src/cryptoadvance/specter/static/styles.css +++ b/src/cryptoadvance/specter/static/styles.css @@ -22,7 +22,8 @@ body { --cmap-border: #506072; --cmap-border-highlight: #4b8cd8; --cmap-border-darker: #405062; - --cmap-bg-address-is-mine: #306d30; + --cmap-bg-address-is-mine: #306d30; + --cmap-bg-highlight: #4a90e2; margin: 0; padding: 0; @@ -268,7 +269,7 @@ nav.side > div > .item { nav.side > a.item.active, nav.side > div > a.item.active { - border-left: 3px solid #4a90e2; + border-left: 3px solid var(--cmap-bg-highlight); background: rgba(0, 0, 0, 0.1); /*color: #fff;*/ } diff --git a/src/cryptoadvance/specter/templates/includes/helpers.jinja b/src/cryptoadvance/specter/templates/includes/helpers.jinja index c4c32667a..9f028cc07 100644 --- a/src/cryptoadvance/specter/templates/includes/helpers.jinja +++ b/src/cryptoadvance/specter/templates/includes/helpers.jinja @@ -76,4 +76,6 @@ + + diff --git a/src/cryptoadvance/specter/templates/includes/sidebar/components/sidebar_services_list.jinja b/src/cryptoadvance/specter/templates/includes/sidebar/components/sidebar_services_list.jinja index 47eb90f40..ee365d1e7 100644 --- a/src/cryptoadvance/specter/templates/includes/sidebar/components/sidebar_services_list.jinja +++ b/src/cryptoadvance/specter/templates/includes/sidebar/components/sidebar_services_list.jinja @@ -1,6 +1,6 @@
{% for _,plugin in specter.service_manager.services.items() %} - {% if plugin.id in current_user.services %} + {% if plugin.id in current_user.services and plugin.visible_in_sidebar %}
{{ plugin.name }}
diff --git a/src/cryptoadvance/specterext/devhelp/templates/devhelp/html/notification-system.jinja b/src/cryptoadvance/specterext/devhelp/templates/devhelp/html/notification-system.jinja new file mode 100644 index 000000000..2c56b27d3 --- /dev/null +++ b/src/cryptoadvance/specterext/devhelp/templates/devhelp/html/notification-system.jinja @@ -0,0 +1,136 @@ +{% extends "base.jinja" %} + +{% block main %} + +

Notification System

+
+

+ The notification System is a flexible way to Notify the user. Other than flask's flash, it's working instantly via WebSockets + and takes advantage of Web Api Notifications. + It has been introduced with #1766 +

+

+ It can e.g. be used in javascript like this(see below for the options): +

+ + + +

+ In python, you would rather use somehing like: +

+ + +
+ +

+ +
+

+ Let's create a Notification: +

+
+ +
+ The Title of the message: +
+

+ +
+ The Text of the message: +
+

+ +
+ {{ _("The target_uis") }}: + + +
    +
  • js_message_box the default
  • +
  • webapi uses Web Api Notifications
  • +
  • js_console same as console.log("bla")
  • +
  • flash same as flash("some message")
  • +
  • logging same as logger.info("Some message")
  • +
+
+
+
+ +

+ +
+ {{ _("The Notification Type") }}: + + + This is called category in flash. Some sort of severity + +
+ +

+ +
+ An optional image: +
+

+
+ Timeout: +
+

+ +

+ +
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/src/cryptoadvance/specterext/devhelp/templates/devhelp/index.jinja b/src/cryptoadvance/specterext/devhelp/templates/devhelp/index.jinja index b8c883b62..543143917 100644 --- a/src/cryptoadvance/specterext/devhelp/templates/devhelp/index.jinja +++ b/src/cryptoadvance/specterext/devhelp/templates/devhelp/index.jinja @@ -19,7 +19,8 @@

- Same for some macros. + Exploring/testing the Notification System +