diff --git a/docs/source/config_overview.md b/docs/source/config_overview.md index 946844f4ca..d2288186ae 100644 --- a/docs/source/config_overview.md +++ b/docs/source/config_overview.md @@ -26,6 +26,10 @@ 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 fe20645cc5..5815ca2ce2 100644 --- a/notebook/app.py +++ b/notebook/app.py @@ -1,11 +1,16 @@ """Jupyter notebook application.""" import os +import re from os.path import join as pjoin 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 +from jupyter_server.extension.handler import ( + ExtensionHandlerJinjaMixin, + ExtensionHandlerMixin, +) 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 @@ -32,6 +37,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() @@ -87,6 +96,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(): @@ -203,6 +213,27 @@ def get(self, path=None): return self.write(tpl) +class CustomCssHandler(NotebookBaseHandler): + """A custom CSS handler.""" + + @web.authenticated + def get(self): + """Get the custom css file.""" + + self.set_header("Content-Type", 'text/css') + page_config = self.get_page_config() + custom_css_file = f"{page_config['jupyterConfigDir']}/custom/custom.css" + + if not os.path.isfile(custom_css_file): + 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()) + + aliases = dict(base_aliases) @@ -227,12 +258,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") @@ -261,6 +305,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) # type:ignore + def server_extension_is_enabled(self, extension): """Check if server extension is enabled.""" try: @@ -290,6 +338,7 @@ def initialize_handlers(self): self.handlers.append(("/edit(.*)", FileHandler)) self.handlers.append(("/consoles/(.*)", ConsoleHandler)) self.handlers.append(("/terminals/(.*)", TerminalHandler)) + self.handlers.append(("/custom/custom.css", CustomCssHandler)) super().initialize_handlers() def initialize(self, argv=None): diff --git a/notebook/custom/custom.css b/notebook/custom/custom.css new file mode 100644 index 0000000000..1c5b24bcdd --- /dev/null +++ b/notebook/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/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 %} diff --git a/notebook/templates/notebooks.html b/notebook/templates/notebooks.html index b2e87d95b6..22107558f1 100644 --- a/notebook/templates/notebooks.html +++ b/notebook/templates/notebooks.html @@ -7,6 +7,10 @@ {% block favicon %} {% endblock %} + + {% if custom_css %} + + {% endif %} diff --git a/notebook/templates/terminals.html b/notebook/templates/terminals.html index 5c2b09f970..fe603df6e8 100644 --- a/notebook/templates/terminals.html +++ b/notebook/templates/terminals.html @@ -7,6 +7,10 @@ {% block favicon %} {% endblock %} + + {% if custom_css %} + + {% endif %} diff --git a/notebook/templates/tree.html b/notebook/templates/tree.html index daa16fb93e..d4cb6f5e55 100644 --- a/notebook/templates/tree.html +++ b/notebook/templates/tree.html @@ -7,6 +7,10 @@ {% block favicon %} {% endblock %} + + {% if custom_css %} + + {% endif %}