From adea3c7f761b656022444d6c7f6bbea2068d5db7 Mon Sep 17 00:00:00 2001 From: RRosio Date: Mon, 17 Apr 2023 10:28:22 -0700 Subject: [PATCH 01/13] wip loading custom css --- .gitignore | 1 + notebook/app.py | 13 ++++++++++++- notebook/static/custom/custom.css | 7 +++++++ notebook/templates/notebooks.html | 1 + notebook/templates/tree.html | 1 + 5 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 notebook/static/custom/custom.css diff --git a/.gitignore b/.gitignore index 683949d8ad..bdf3d08496 100644 --- a/.gitignore +++ b/.gitignore @@ -115,6 +115,7 @@ junit.xml [uU]ntitled* notebook/static/* !notebook/static/favicons +!notebook/static/custom notebook/labextension notebook/schemas docs/source/changelog.md diff --git a/notebook/app.py b/notebook/app.py index e4ca63469d..84c00a396f 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -5,7 +5,11 @@ from jupyter_client.utils import ensure_async from jupyter_core.application import base_aliases from jupyter_server.base.handlers import JupyterHandler -from jupyter_server.extension.handler import ExtensionHandlerJinjaMixin, ExtensionHandlerMixin +from jupyter_server.extension.handler import ( + ExtensionHandlerJinjaMixin, + ExtensionHandlerMixin, + FileFindHandler, +) from jupyter_server.serverapp import flags from jupyter_server.utils import url_escape, url_is_absolute from jupyter_server.utils import url_path_join as ujoin @@ -276,6 +280,13 @@ def initialize_handlers(self): self.handlers.append(("/edit(.*)", FileHandler)) self.handlers.append(("/consoles/(.*)", ConsoleHandler)) self.handlers.append(("/terminals/(.*)", TerminalHandler)) + self.handlers.append( + ( + ujoin(self.default_url, r"/custom/(.*)"), + FileFindHandler, + {'path': self._default_static_dir, 'no_cache_paths': ['/']}, + ) + ) super().initialize_handlers() def initialize(self, argv=None): diff --git a/notebook/static/custom/custom.css b/notebook/static/custom/custom.css new file mode 100644 index 0000000000..1c5b24bcdd --- /dev/null +++ b/notebook/static/custom/custom.css @@ -0,0 +1,7 @@ +/* +Placeholder for custom user CSS + +mainly to be overridden in profile/static/custom/custom.css + +This will always be an empty file +*/ diff --git a/notebook/templates/notebooks.html b/notebook/templates/notebooks.html index b2e87d95b6..3c0ee4659d 100644 --- a/notebook/templates/notebooks.html +++ b/notebook/templates/notebooks.html @@ -7,6 +7,7 @@ {% block favicon %} {% endblock %} + diff --git a/notebook/templates/tree.html b/notebook/templates/tree.html index daa16fb93e..b1e9561d3e 100644 --- a/notebook/templates/tree.html +++ b/notebook/templates/tree.html @@ -7,6 +7,7 @@ {% block favicon %} {% endblock %} + From 06c8c435745cb6fdfb3f2700cb0b82fb867c3cad Mon Sep 17 00:00:00 2001 From: RRosio Date: Tue, 16 May 2023 20:10:17 +0100 Subject: [PATCH 02/13] read css file from profile directory and apply --- notebook/app.py | 25 +++++++++++++++++-------- notebook/templates/notebooks.html | 2 +- notebook/templates/terminals.html | 1 + notebook/templates/tree.html | 3 ++- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/notebook/app.py b/notebook/app.py index 84c00a396f..689194a03d 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -4,11 +4,11 @@ from jupyter_client.utils import ensure_async from jupyter_core.application import base_aliases +from jupyter_core.paths import jupyter_config_dir from jupyter_server.base.handlers import JupyterHandler from jupyter_server.extension.handler import ( ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, - FileFindHandler, ) from jupyter_server.serverapp import flags from jupyter_server.utils import url_escape, url_is_absolute @@ -51,6 +51,7 @@ def get_page_config(self): "fullStaticUrl": ujoin(self.base_url, "static", self.name), "frontendUrl": ujoin(self.base_url, "/"), "exposeAppInBrowser": app.expose_app_in_browser, + "jupyter_configDir": jupyter_config_dir(), } if "hub_prefix" in app.serverapp.tornado_settings: @@ -91,6 +92,7 @@ def get_page_config(self): page_config.setdefault("mathjaxConfig", mathjax_config) page_config.setdefault("fullMathjaxUrl", mathjax_url) + page_config.setdefault("jupyterConfigDir", jupyter_config_dir()) # Put all our config in page_config for name in config.trait_names(): @@ -207,6 +209,19 @@ def get(self, path=None): return self.write(tpl) +class CustomCssHandler(NotebookBaseHandler): + """A redirect handler.""" + + @web.authenticated + def get(self): + """Get the custom css file.""" + + self.set_header("Content-Type", 'text/css') + page_conf = self.get_page_config() + with open(f"{page_conf['jupyterConfigDir']}/custom/custom.css") as css_file: + self.write(css_file.read()) + + aliases = dict(base_aliases) @@ -280,13 +295,7 @@ def initialize_handlers(self): self.handlers.append(("/edit(.*)", FileHandler)) self.handlers.append(("/consoles/(.*)", ConsoleHandler)) self.handlers.append(("/terminals/(.*)", TerminalHandler)) - self.handlers.append( - ( - ujoin(self.default_url, r"/custom/(.*)"), - FileFindHandler, - {'path': self._default_static_dir, 'no_cache_paths': ['/']}, - ) - ) + self.handlers.append(("/custom/custom.css", CustomCssHandler)) super().initialize_handlers() def initialize(self, argv=None): diff --git a/notebook/templates/notebooks.html b/notebook/templates/notebooks.html index 3c0ee4659d..2c27522911 100644 --- a/notebook/templates/notebooks.html +++ b/notebook/templates/notebooks.html @@ -7,7 +7,7 @@ {% block favicon %} {% endblock %} - + diff --git a/notebook/templates/terminals.html b/notebook/templates/terminals.html index 5c2b09f970..0d1e0034d9 100644 --- a/notebook/templates/terminals.html +++ b/notebook/templates/terminals.html @@ -7,6 +7,7 @@ {% block favicon %} {% endblock %} + diff --git a/notebook/templates/tree.html b/notebook/templates/tree.html index b1e9561d3e..8af3215a0b 100644 --- a/notebook/templates/tree.html +++ b/notebook/templates/tree.html @@ -7,7 +7,8 @@ {% block favicon %} {% endblock %} - + + From 9cf953e501286fde02f12dd3b2c3e05b7c58e08f Mon Sep 17 00:00:00 2001 From: RRosio Date: Mon, 5 Jun 2023 22:41:25 -0700 Subject: [PATCH 03/13] update css handler, add CLI flag to disable custom CSS, documentation --- docs/source/config_overview.md | 5 +++++ notebook/app.py | 35 ++++++++++++++++++++++++++----- notebook/templates/notebooks.html | 3 +++ notebook/templates/terminals.html | 3 +++ notebook/templates/tree.html | 4 +++- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/docs/source/config_overview.md b/docs/source/config_overview.md index 946844f4ca..5d1e13e7fa 100644 --- a/docs/source/config_overview.md +++ b/docs/source/config_overview.md @@ -26,6 +26,11 @@ and editing settings is similar for all the Jupyter applications. > - [traitlets](https://traitlets.readthedocs.io/en/latest/config.html#module-traitlets.config) > provide a low-level architecture for configuration. +### Disabling Custom CSS + +Custom CSS is loaded by default as was done with Jupyter Notebook 6. In the jupyter configuration directory, the `/.jupyter/custom/custom.css` file will be loaded unless the the application is initialized with the `custom_css` flag with the argument set to `False` as in `--JupyterNotebookApp.custom_css=False`. + + (configure-jupyter-server)= ## Jupyter server diff --git a/notebook/app.py b/notebook/app.py index 689194a03d..00bbbcf439 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -36,6 +36,10 @@ class NotebookBaseHandler(ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, JupyterHandler): """The base notebook API handler.""" + @property + def custom_css(self): + return self.settings.get("custom_css", True) + def get_page_config(self): """Get the page config.""" config = LabConfig() @@ -51,7 +55,6 @@ def get_page_config(self): "fullStaticUrl": ujoin(self.base_url, "static", self.name), "frontendUrl": ujoin(self.base_url, "/"), "exposeAppInBrowser": app.expose_app_in_browser, - "jupyter_configDir": jupyter_config_dir(), } if "hub_prefix" in app.serverapp.tornado_settings: @@ -210,16 +213,21 @@ def get(self, path=None): class CustomCssHandler(NotebookBaseHandler): - """A redirect handler.""" + """A custom CSS handler.""" @web.authenticated def get(self): """Get the custom css file.""" self.set_header("Content-Type", 'text/css') - page_conf = self.get_page_config() - with open(f"{page_conf['jupyterConfigDir']}/custom/custom.css") as css_file: - self.write(css_file.read()) + page_config = self.get_page_config() + custom_css_file = f"{page_config['jupyterConfigDir']}/custom/custom.css" + + if not os.path.isfile(custom_css_file): + custom_css_file = f"{page_config['staticDir']}/custom/custom.css" + + with open(custom_css_file) as css_f: + return self.write(css_f.read()) aliases = dict(base_aliases) @@ -246,12 +254,25 @@ class JupyterNotebookApp(NotebookConfigShimMixin, LabServerApp): help="Whether to expose the global app instance to browser via window.jupyterapp", ) + custom_css = Bool( + True, + config=True, + help="""Whether custom CSS is loaded on the page. + Defaults to True and custom CSS is loaded. + """, + ) + flags = flags flags["expose-app-in-browser"] = ( {"JupyterNotebookApp": {"expose_app_in_browser": True}}, "Expose the global app instance to browser via window.jupyterapp.", ) + flags["custom-css"] = ( + {"JupyterNotebookApp": {"custom_css": True}}, + "Load custom CSS in template html files. Default is True", + ) + @default("static_dir") def _default_static_dir(self): return os.path.join(HERE, "static") @@ -280,6 +301,10 @@ def _default_user_settings_dir(self): def _default_workspaces_dir(self): return get_workspaces_dir() + def _prepare_templates(self): + super(LabServerApp, self)._prepare_templates() + self.jinja2_env.globals.update(custom_css=self.custom_css) + def initialize_handlers(self): """Initialize handlers.""" self.handlers.append( diff --git a/notebook/templates/notebooks.html b/notebook/templates/notebooks.html index 2c27522911..22107558f1 100644 --- a/notebook/templates/notebooks.html +++ b/notebook/templates/notebooks.html @@ -7,7 +7,10 @@ {% block favicon %} {% endblock %} + + {% if custom_css %} + {% endif %} diff --git a/notebook/templates/terminals.html b/notebook/templates/terminals.html index 0d1e0034d9..fe603df6e8 100644 --- a/notebook/templates/terminals.html +++ b/notebook/templates/terminals.html @@ -7,7 +7,10 @@ {% block favicon %} {% endblock %} + + {% if custom_css %} + {% endif %} diff --git a/notebook/templates/tree.html b/notebook/templates/tree.html index 8af3215a0b..d4cb6f5e55 100644 --- a/notebook/templates/tree.html +++ b/notebook/templates/tree.html @@ -7,8 +7,10 @@ {% block favicon %} {% endblock %} - + {% if custom_css %} + + {% endif %} From 619e7f6b96e8b21585107a51ae7e1ae2a08778ed Mon Sep 17 00:00:00 2001 From: RRosio Date: Tue, 6 Jun 2023 01:08:56 -0700 Subject: [PATCH 04/13] import ExtensionAppJinjaMixin --- notebook/app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/notebook/app.py b/notebook/app.py index 00bbbcf439..6e0b3b2175 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -6,6 +6,7 @@ from jupyter_core.application import base_aliases from jupyter_core.paths import jupyter_config_dir from jupyter_server.base.handlers import JupyterHandler +from jupyter_server.extension.application import ExtensionAppJinjaMixin from jupyter_server.extension.handler import ( ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, @@ -233,7 +234,7 @@ def get(self): aliases = dict(base_aliases) -class JupyterNotebookApp(NotebookConfigShimMixin, LabServerApp): +class JupyterNotebookApp(NotebookConfigShimMixin, ExtensionAppJinjaMixin, LabServerApp): """The notebook server extension app.""" name = "notebook" From 23e55b30c3fc621c9dc9b40e109285e3a15fc79b Mon Sep 17 00:00:00 2001 From: RRosio Date: Tue, 6 Jun 2023 01:18:33 -0700 Subject: [PATCH 05/13] remove ExtensionAppJinjaMixin as a JupyterNotebookApp base class --- notebook/app.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/notebook/app.py b/notebook/app.py index 6e0b3b2175..00bbbcf439 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -6,7 +6,6 @@ from jupyter_core.application import base_aliases from jupyter_core.paths import jupyter_config_dir from jupyter_server.base.handlers import JupyterHandler -from jupyter_server.extension.application import ExtensionAppJinjaMixin from jupyter_server.extension.handler import ( ExtensionHandlerJinjaMixin, ExtensionHandlerMixin, @@ -234,7 +233,7 @@ def get(self): aliases = dict(base_aliases) -class JupyterNotebookApp(NotebookConfigShimMixin, ExtensionAppJinjaMixin, LabServerApp): +class JupyterNotebookApp(NotebookConfigShimMixin, LabServerApp): """The notebook server extension app.""" name = "notebook" From 0eeebdb1abf09328bdbb1622c165b1c30186b88a Mon Sep 17 00:00:00 2001 From: RRosio Date: Tue, 6 Jun 2023 01:29:08 -0700 Subject: [PATCH 06/13] add type hint to ignore jinja2_env type --- notebook/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebook/app.py b/notebook/app.py index 00bbbcf439..6d52f4b08b 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -303,7 +303,7 @@ def _default_workspaces_dir(self): def _prepare_templates(self): super(LabServerApp, self)._prepare_templates() - self.jinja2_env.globals.update(custom_css=self.custom_css) + self.jinja2_env.globals.update(custom_css=self.custom_css) # type:ignore def initialize_handlers(self): """Initialize handlers.""" From e99e1a3c66094dfcb33cdda41de7d2f57ced4ce8 Mon Sep 17 00:00:00 2001 From: RRosio Date: Tue, 6 Jun 2023 01:48:22 -0700 Subject: [PATCH 07/13] fix prettier issue in docs --- docs/source/config_overview.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/config_overview.md b/docs/source/config_overview.md index 5d1e13e7fa..d2288186ae 100644 --- a/docs/source/config_overview.md +++ b/docs/source/config_overview.md @@ -30,7 +30,6 @@ and editing settings is similar for all the Jupyter applications. Custom CSS is loaded by default as was done with Jupyter Notebook 6. In the jupyter configuration directory, the `/.jupyter/custom/custom.css` file will be loaded unless the the application is initialized with the `custom_css` flag with the argument set to `False` as in `--JupyterNotebookApp.custom_css=False`. - (configure-jupyter-server)= ## Jupyter server From eb7ff71d0e86e2dc68b69446268a3e229f641e46 Mon Sep 17 00:00:00 2001 From: RRosio Date: Tue, 6 Jun 2023 12:01:36 -0700 Subject: [PATCH 08/13] add empty line; trigger new docs build --- docs/source/config_overview.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/config_overview.md b/docs/source/config_overview.md index d2288186ae..5d1e13e7fa 100644 --- a/docs/source/config_overview.md +++ b/docs/source/config_overview.md @@ -30,6 +30,7 @@ and editing settings is similar for all the Jupyter applications. Custom CSS is loaded by default as was done with Jupyter Notebook 6. In the jupyter configuration directory, the `/.jupyter/custom/custom.css` file will be loaded unless the the application is initialized with the `custom_css` flag with the argument set to `False` as in `--JupyterNotebookApp.custom_css=False`. + (configure-jupyter-server)= ## Jupyter server From ae7badcfb34ba30f2b765ed3206624825fbcf96d Mon Sep 17 00:00:00 2001 From: RRosio Date: Tue, 6 Jun 2023 12:28:01 -0700 Subject: [PATCH 09/13] new line caused prettier error, remove --- docs/source/config_overview.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/config_overview.md b/docs/source/config_overview.md index 5d1e13e7fa..d2288186ae 100644 --- a/docs/source/config_overview.md +++ b/docs/source/config_overview.md @@ -30,7 +30,6 @@ and editing settings is similar for all the Jupyter applications. Custom CSS is loaded by default as was done with Jupyter Notebook 6. In the jupyter configuration directory, the `/.jupyter/custom/custom.css` file will be loaded unless the the application is initialized with the `custom_css` flag with the argument set to `False` as in `--JupyterNotebookApp.custom_css=False`. - (configure-jupyter-server)= ## Jupyter server From 594e678cac51bd74da7c4b8c0d14a7a08f1d1a14 Mon Sep 17 00:00:00 2001 From: RRosio Date: Wed, 7 Jun 2023 17:24:06 -0700 Subject: [PATCH 10/13] move custom css to different directory and update handler --- .gitignore | 1 - notebook/app.py | 4 +++- notebook/{static => }/custom/custom.css | 0 3 files changed, 3 insertions(+), 2 deletions(-) rename notebook/{static => }/custom/custom.css (100%) diff --git a/.gitignore b/.gitignore index bdf3d08496..683949d8ad 100644 --- a/.gitignore +++ b/.gitignore @@ -115,7 +115,6 @@ junit.xml [uU]ntitled* notebook/static/* !notebook/static/favicons -!notebook/static/custom notebook/labextension notebook/schemas docs/source/changelog.md diff --git a/notebook/app.py b/notebook/app.py index 328266a4be..e088182c1b 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -1,5 +1,6 @@ """Jupyter notebook application.""" import os +import re from os.path import join as pjoin from jupyter_client.utils import ensure_async @@ -224,7 +225,8 @@ def get(self): custom_css_file = f"{page_config['jupyterConfigDir']}/custom/custom.css" if not os.path.isfile(custom_css_file): - custom_css_file = f"{page_config['staticDir']}/custom/custom.css" + custom_dir = re.match('^(.*?)/static', page_config['staticDir']).groups()[0] + custom_css_file = f"{custom_dir}/custom/custom.css" with open(custom_css_file) as css_f: return self.write(css_f.read()) diff --git a/notebook/static/custom/custom.css b/notebook/custom/custom.css similarity index 100% rename from notebook/static/custom/custom.css rename to notebook/custom/custom.css From 2793e4569d70bbd1da7e5dcebb5ed8a9c1704d18 Mon Sep 17 00:00:00 2001 From: RRosio Date: Wed, 7 Jun 2023 17:43:29 -0700 Subject: [PATCH 11/13] modify regex --- notebook/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notebook/app.py b/notebook/app.py index e088182c1b..b54a74abdb 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -225,8 +225,8 @@ def get(self): custom_css_file = f"{page_config['jupyterConfigDir']}/custom/custom.css" if not os.path.isfile(custom_css_file): - custom_dir = re.match('^(.*?)/static', page_config['staticDir']).groups()[0] - custom_css_file = f"{custom_dir}/custom/custom.css" + custom_dir = re.match('^(.*?)static', page_config['staticDir']).groups()[0] + custom_css_file = f"{custom_dir}custom/custom.css" with open(custom_css_file) as css_f: return self.write(css_f.read()) From f76c807c2f448dae367a819f43878a3b8de9e8c9 Mon Sep 17 00:00:00 2001 From: RRosio Date: Wed, 7 Jun 2023 18:08:32 -0700 Subject: [PATCH 12/13] satisfy mypy: check for match in regex before grabbing the directory path --- notebook/app.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/notebook/app.py b/notebook/app.py index b54a74abdb..8b416a1be1 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -225,8 +225,10 @@ def get(self): custom_css_file = f"{page_config['jupyterConfigDir']}/custom/custom.css" if not os.path.isfile(custom_css_file): - custom_dir = re.match('^(.*?)static', page_config['staticDir']).groups()[0] - custom_css_file = f"{custom_dir}custom/custom.css" + static_path_root = re.match('^(.*?)static', page_config['staticDir']) + if static_path_root is not None: + custom_dir = static_path_root.groups()[0] + custom_css_file = f"{custom_dir}custom/custom.css" with open(custom_css_file) as css_f: return self.write(css_f.read()) From d56964d3972a081f3bf9d55e507e8e2844f77bc7 Mon Sep 17 00:00:00 2001 From: RRosio Date: Thu, 8 Jun 2023 08:02:42 -0700 Subject: [PATCH 13/13] load custom css in consoles page --- notebook/templates/consoles.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/notebook/templates/consoles.html b/notebook/templates/consoles.html index 0dd0cb8288..00fa72d3e5 100644 --- a/notebook/templates/consoles.html +++ b/notebook/templates/consoles.html @@ -7,6 +7,10 @@ {% block favicon %} {% endblock %} + + {% if custom_css %} + + {% endif %}