add and run djlint formatter

This commit is contained in:
Min RK
2024-04-18 17:49:07 +02:00
parent fb1614e20a
commit d9ce1b917f
20 changed files with 740 additions and 842 deletions

View File

@@ -33,6 +33,15 @@ repos:
rev: v4.0.0-alpha.8 rev: v4.0.0-alpha.8
hooks: hooks:
- id: prettier - id: prettier
exclude: .*/templates/.*
# autoformat HTML templates
- repo: https://github.com/djlint/djLint
rev: v1.34.1
hooks:
- id: djlint-reformat-jinja
files: ".*templates/.*.html"
types_or: ["html"]
# Autoformat and linting, misc. details # Autoformat and linting, misc. details
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks

View File

@@ -1,26 +1,24 @@
{%- extends "!layout.html" %} {%- extends "!layout.html" %}
{# not sure why, but theme CSS prevents scrolling within redoc content {# not sure why, but theme CSS prevents scrolling within redoc content
# If this were fixed, we could keep the navbar and footer # If this were fixed, we could keep the navbar and footer
#} #}
{%- block css %}{%- endblock %} {% block css %}
{%- block docs_navbar %}{%- endblock %} {% endblock css %}
{%- block footer %}{% endblock %} {% block docs_navbar %}
{%- block body_tag %} {% endblock docs_navbar %}
<body> {% block footer %}
{%- endblock %} {% endblock footer %}
{# djlint: off #}
{%- block body_tag -%}<body>{%- endblock body_tag %}
{# djlint: on #}
{%- block extrahead %} {%- block extrahead %}
{{ super() }} {{ super() }}
<link href="{{ pathto('_static/redoc-fonts.css', 1) }}" <link href="{{ pathto('_static/redoc-fonts.css', 1) }}" rel="stylesheet" />
rel="stylesheet" <script src="{{ pathto('_static/redoc.js', 1) }}"></script>
/> {%- endblock extrahead %}
<script src="{{ pathto('_static/redoc.js', 1) }}"></script>
{%- endblock %}
{%- block content %} {%- block content %}
<redoc id="redoc-spec"></redoc> <redoc id="redoc-spec"></redoc>
<script> <script>
if (location.protocol === "file:") { if (location.protocol === "file:") {
document.body.innerText = "Rendered API specification doesn't work with file: protocol. Use sphinx-autobuild to do local builds of the docs, served over HTTP." document.body.innerText = "Rendered API specification doesn't work with file: protocol. Use sphinx-autobuild to do local builds of the docs, served over HTTP."
} else { } else {
@@ -30,5 +28,5 @@
document.getElementById("redoc-spec"), document.getElementById("redoc-spec"),
); );
} }
</script> </script>
{%- endblock %} {%- endblock content %}

View File

@@ -1,9 +1,10 @@
{% extends "templates/page.html" %} {% block announcement %} {% extends "templates/page.html" %}
<div class="container text-center announcement"></div> {% block announcement %}<div class="container text-center announcement"></div>{% endblock %}
{% endblock %} {% block script %} {{ super() }} {% block script %}
<script> {{ super() }}
<script>
$.get("/services/announcement/", function (data) { $.get("/services/announcement/", function (data) {
$(".announcement").html(data["announcement"]); $(".announcement").html(data["announcement"]);
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -2,11 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>JupyterHub</title> <title>JupyterHub</title>
<meta http-equiv="X-UA-Compatible" content="chrome=1" /> <meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="/static/css/style.min.css" type="text/css" /> <link rel="stylesheet" href="/static/css/style.min.css" type="text/css" />
</head> </head>
<body> <body>

View File

@@ -7,30 +7,23 @@ It makes the following modifications:
- update logo url to jupyterhub - update logo url to jupyterhub
- remove `?redirects` url param that may be added by jupyterhub - remove `?redirects` url param that may be added by jupyterhub
#} #}
{% extends "templates/page.html" %} {% extends "templates/page.html" %}
{% block header_buttons %} {% block header_buttons %}
{{ super() }} {{ super() }}
<span> <span>
<a <a href="{{ hub_control_panel_url }}"
href="{{hub_control_panel_url}}" id="jupyterhub-control-panel-link"
id="jupyterhub-control-panel-link" class="btn btn-default btn-sm navbar-btn pull-right"
class="btn btn-default btn-sm navbar-btn pull-right" style="margin-right: 4px;
style="margin-right: 4px; margin-left: 2px" margin-left: 2px">Control Panel</a>
> </span>
Control Panel
</a>
</span>
{% endblock %} {% endblock %}
{% block logo %} {% block logo %}
<img src="{{logo_url}}" alt="Jupyter Notebook" /> <img src="{{ logo_url }}" alt="Jupyter Notebook" />
{% endblock logo %} {% endblock logo %}
{% block script %} {% block script %}
{{ super() }} {{ super() }}
<script type="text/javascript"> <script type="text/javascript">
function _remove_redirects_param() { function _remove_redirects_param() {
// remove ?redirects= param from URL so that // remove ?redirects= param from URL so that
// successful page loads don't increment the redirect loop counter // successful page loads don't increment the redirect loop counter
@@ -57,5 +50,5 @@ It makes the following modifications:
} }
} }
_remove_redirects_param(); _remove_redirects_param();
</script> </script>
{% endblock script %} {% endblock script %}

View File

@@ -120,6 +120,11 @@ select = [
"F", # flake8 "F", # flake8
] ]
# djlint lints/formats our jinja templates
# https://djlint.com/docs/configuration/
[tool.djlint]
indent = 2
# tbump is used to simplify and standardize the release process when updating # tbump is used to simplify and standardize the release process when updating
# the version, making a git commit and tag, and pushing changes. # the version, making a git commit and tag, and pushing changes.
# #

View File

@@ -1,5 +1,2 @@
{% extends "error.html" %} {% extends "error.html" %}
{% block error_detail %}<p>Jupyter has lots of moons, but this is not one...</p>{% endblock %}
{% block error_detail %}
<p>Jupyter has lots of moons, but this is not one...</p>
{% endblock %}

View File

@@ -1,55 +1,51 @@
{% extends "page.html" %} {% extends "page.html" %}
{% block login_widget %} {% block login_widget %}{% endblock %}
{% endblock %}
{% block main %} {% block main %}
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<h1 class="text-center">Accept sharing invitation</h1> <h1 class="text-center">Accept sharing invitation</h1>
<p class="lead"> <p class="lead">
You ({{ user.name }}) have been invited to access {{ owner.name }}'s server You ({{ user.name }}) have been invited to access {{ owner.name }}'s server
{%- if spawner.name %} ({{ spawner.name }}){%- endif %} at <a href="{{ spawner_url | safe }}">{{ spawner_url }}</a> {%- if spawner.name %}({{ spawner.name }}){%- endif %} at <a href="{{ spawner_url | safe }}">{{ spawner_url }}</a>
</p> </p>
{% if not spawner_ready %}
{% if not spawner_ready %} <p class="alert alert-danger">
<p class="alert alert-danger"> The server at {{ spawner_url }} is not currently running.
The server at {{ spawner_url }} is not currently running. After accepting permission, you may need to ask {{ owner.name }}
After accepting permission, you may need to ask {{ owner.name }} to start the server before you can access it.
to start the server before you can access it. </p>
</p> {% endif %}
{% endif %} <form method="POST" action="">
<div class="card">
<form method="POST" action=""> <div class="card-header">
<div class="card"> By accepting the invitation, you will be granted the following permissions,
<div class="card-header"> restricted to this particular server:
By accepting the invitation, you will be granted the following permissions, </div>
restricted to this particular server: <div class="card-body">
</div> {# these are the 'real' inputs to the form -#}
<div class="card-body"> <input type="hidden" name="_xsrf" value="{{ xsrf }}" />
{# these are the 'real' inputs to the form -#} <input type="hidden" name="code" value="{{ code }}" />
<input type="hidden" name="_xsrf" value="{{ xsrf }}" /> {% for scope_info in scope_descriptions -%}
<input type="hidden" name="code" value="{{ code }}" /> <div class="form-check input-group">
{% for scope_info in scope_descriptions -%} <label>
<div class="form-check input-group"> <span>
<label> {{ scope_info['description'] }}
<span> {% if scope_info['filter'] %}Applies to {{ scope_info['filter'] }}.{% endif %}
{{ scope_info['description'] }} </span>
{% if scope_info['filter'] %} </label>
Applies to {{ scope_info['filter'] }}. {% endif %} </div>
</span> {% endfor -%}
</label> </div>
<div class="card-footer">
<button type="submit" class="form-control btn btn-jupyter">Accept invitation</button>
<p class="small">
After accepting the invitation, you will be redirected to <a href="{{ next_url | safe }}">{{ next_url }}</a>.
</p>
</div>
</div>
</form>
</div> </div>
{% endfor -%}
</div>
<div class="card-footer">
<button type="submit" class="form-control btn btn-jupyter" >Accept invitation</button>
<p class="small">
After accepting the invitation, you will be redirected to <a href="{{ next_url | safe }}">{{ next_url }}</a>.
</p>
</div> </div>
</div> </div>
</form>
</div></div></div>
{% endblock %} {% endblock %}

View File

@@ -1,17 +1,13 @@
{% extends "page.html" %} {% extends "page.html" %}
{% block main %} {% block main %}
<div id="react-admin-hook"> <div id="react-admin-hook">
<script id="jupyterhub-admin-config"> <script id="jupyterhub-admin-config">
window.api_page_limit = parseInt("{{ api_page_limit|safe }}") window.api_page_limit = parseInt("{{ api_page_limit|safe }}")
window.base_url = "{{ base_url|safe }}" window.base_url = "{{ base_url|safe }}"
</script> </script>
<script src={{ static_url("js/admin-react.js") }}></script> <script src={{ static_url("js/admin-react.js") }}></script>
</div> </div>
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
<div class="py-2 px-4 bg-body-tertiary small version_footer"> <div class="py-2 px-4 bg-body-tertiary small version_footer">JupyterHub {{ server_version }}</div>
JupyterHub {{ server_version }}
</div>
{% endblock %} {% endblock %}

View File

@@ -1,40 +1,19 @@
{% extends "page.html" %} {% extends "page.html" %}
{% block login_widget %}{% endblock %}
{% block login_widget %}
{% endblock %}
{% block main %} {% block main %}
<div class="error">
<div class="error"> {% block h1_error %}
{% block h1_error %} <h1>{{ status_code }} : {{ status_message }}</h1>
<h1> {% endblock h1_error %}
{{status_code}} : {{status_message}} {% block error_detail %}
</h1> {% if message %}<p>{{ message }}</p>{% endif %}
{% endblock h1_error %} {% if message_html %}<p>{{ message_html | safe }}</p>{% endif %}
{% block error_detail %} {% if extra_error_html %}<p>{{ extra_error_html | safe }}</p>{% endif %}
{% if message %} {% endblock error_detail %}
<p> </div>
{{message}}
</p>
{% endif %}
{% if message_html %}
<p>
{{message_html | safe}}
</p>
{% endif %}
{% if extra_error_html %}
<p>
{{extra_error_html | safe}}
</p>
{% endif %}
{% endblock error_detail %}
</div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
{{super()}} {{ super() }}
<script type="text/javascript"> <script type="text/javascript">
function _remove_redirects_from_url() { function _remove_redirects_from_url() {
if (window.location.search.length <= 1) { if (window.location.search.length <= 1) {

View File

@@ -2,93 +2,89 @@
{% if announcement_home is string %} {% if announcement_home is string %}
{% set announcement = announcement_home %} {% set announcement = announcement_home %}
{% endif %} {% endif %}
{% block main %} {% block main %}
<div class="container"> <div class="container">
<h1 class="sr-only">JupyterHub home page</h1> <h1 class="sr-only">JupyterHub home page</h1>
<div class="row"> <div class="row">
<div class="text-center"> <div class="text-center">
{% if default_server.active %} {% if default_server.active %}<a id="stop" role="button" class="btn btn-lg btn-danger">Stop My Server</a>{% endif %}
<a id="stop" role="button" class="btn btn-lg btn-danger"> <a id="start"
Stop My Server role="button"
class="btn btn-lg btn-primary"
href="{{ url }}">
{% if not default_server.active %}Start{% endif %}
My Server
</a> </a>
{% endif %} </div>
<a id="start" role="button" class="btn btn-lg btn-primary" href="{{ url }}">
{% if not default_server.active %}Start{% endif %}
My Server
</a>
</div> </div>
{% if allow_named_servers %}
<h2>Named Servers</h2>
<p>
In addition to your default server,
you may have additional
{% if named_server_limit_per_user > 0 %}{{ named_server_limit_per_user }}{% endif %}
server(s) with names.
This allows you to have more than one server running at the same time.
</p>
{% set named_spawners = user.all_spawners(include_default=False)|list %}
<table class="server-table table table-striped">
<thead>
<tr>
<th>Server name</th>
<th>URL</th>
<th>Last activity</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr class="home-server-row add-server-row">
<td colspan="4">
<input class="new-server-name"
aria-label="server name"
placeholder="name-your-server">
<button role="button"
type="button"
class="new-server-btn btn btn-xs btn-primary">Add New Server</button>
</td>
</tr>
{% for spawner in named_spawners %}
<tr class="home-server-row" data-server-name="{{ spawner.name }}">
{# name #}
<td>{{ spawner.name }}</td>
{# url #}
<td>
<a class="server-link {% if not spawner.ready %}hidden{% endif %}"
href="{{ user.server_url(spawner.name) }}">{{ user.server_url(spawner.name) }}</a>
</td>
{# activity #}
<td class='time-col'>
{% if spawner.last_activity %}
{{ spawner.last_activity.isoformat() + 'Z' }}
{% else %}
Never
{% endif %}
</td>
{# actions #}
<td>
<a role="button"
class="stop-server btn btn-xs btn-danger{% if not spawner.active %} hidden{% endif %}"
id="stop-{{ spawner.name }}">stop</a>
<a role="button"
class="start-server btn btn-xs btn-primary {% if spawner.active %}hidden{% endif %}"
id="start-{{ spawner.name }}"
href="{{ base_url }}spawn/{{ user.name }}/{{ spawner.name }}">start</a>
<button role="button"
class="delete-server btn btn-xs btn-danger{% if spawner.active %} hidden{% endif %}"
id="delete-{{ spawner.name }}">delete</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div> </div>
{% if allow_named_servers %}
<h2>
Named Servers
</h2>
<p>
In addition to your default server,
you may have additional {% if named_server_limit_per_user > 0 %}{{ named_server_limit_per_user }} {% endif %}server(s) with names.
This allows you to have more than one server running at the same time.
</p>
{% set named_spawners = user.all_spawners(include_default=False)|list %}
<table class="server-table table table-striped">
<thead>
<tr>
<th>Server name</th>
<th>URL</th>
<th>Last activity</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr class="home-server-row add-server-row">
<td colspan="4">
<input class="new-server-name" aria-label="server name" placeholder="name-your-server">
<button role="button" type="button" class="new-server-btn btn btn-xs btn-primary">
Add New Server
</button>
</td>
</tr>
{% for spawner in named_spawners %}
<tr class="home-server-row" data-server-name="{{ spawner.name }}">
{# name #}
<td>{{ spawner.name }}</td>
{# url #}
<td>
<a class="server-link {% if not spawner.ready %}hidden{% endif %}" href="{{ user.server_url(spawner.name) }}">
{{ user.server_url(spawner.name) }}
</a>
</td>
{# activity #}
<td class='time-col'>
{% if spawner.last_activity %}
{{ spawner.last_activity.isoformat() + 'Z' }}
{% else %}
Never
{% endif %}
</td>
{# actions #}
<td>
<a role="button" class="stop-server btn btn-xs btn-danger{% if not spawner.active %} hidden{% endif %}" id="stop-{{ spawner.name }}">stop</a>
<a role="button" class="start-server btn btn-xs btn-primary {% if spawner.active %} hidden{% endif %}" id="start-{{ spawner.name }}"
href="{{ base_url }}spawn/{{ user.name }}/{{ spawner.name }}"
>
start
</a>
<button role="button" class="delete-server btn btn-xs btn-danger{% if spawner.active %} hidden{% endif %}" id="delete-{{ spawner.name }}">delete</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
{% endblock main %} {% endblock main %}
{% block script %} {% block script %}
{{ super() }} {{ super() }}
<script type="text/javascript"> <script type="text/javascript">require(["home"]);</script>
require(["home"]);
</script>
{% endblock %} {% endblock %}

View File

@@ -2,112 +2,94 @@
{% if announcement_login is string %} {% if announcement_login is string %}
{% set announcement = announcement_login %} {% set announcement = announcement_login %}
{% endif %} {% endif %}
{% block login_widget %}{% endblock %}
{% block login_widget %}
{% endblock %}
{% block main %} {% block main %}
{% block login %}
{% block login %} <div id="login-main" class="container">
<div id="login-main" class="container"> {% block login_container %}
{% block login_container %} {% if custom_html %}
{% if custom_html %} {{ custom_html | safe }}
{{ custom_html | safe }} {% elif login_service %}
{% elif login_service %} <div class="service-login">
<div class="service-login"> <p id='insecure-login-warning' class='hidden'>
<p id='insecure-login-warning' class='hidden'> Warning: JupyterHub seems to be served over an unsecured HTTP connection.
Warning: JupyterHub seems to be served over an unsecured HTTP connection. We strongly recommend enabling HTTPS for JupyterHub.
We strongly recommend enabling HTTPS for JupyterHub. </p>
</p> <a role="button"
class='btn btn-jupyter btn-lg'
<a role="button" class='btn btn-jupyter btn-lg' href='{{ authenticator_login_url | safe }}'> href='{{ authenticator_login_url | safe }}'>Sign in with {{ login_service }}</a>
Sign in with {{login_service}} </div>
</a> {% else %}
</div> <form action="{{ authenticator_login_url | safe }}"
{% else %} method="post"
<form action="{{ authenticator_login_url | safe }}" method="post" role="form"> role="form">
<div class="auth-form-header"> <div class="auth-form-header">
<h1>Sign in</h1> <h1>Sign in</h1>
</div> </div>
<div class='auth-form-body m-auto'> <div class='auth-form-body m-auto'>
<p id='insecure-login-warning' class='hidden'>
<p id='insecure-login-warning' class='hidden'> Warning: JupyterHub seems to be served over an unsecured HTTP connection.
Warning: JupyterHub seems to be served over an unsecured HTTP connection. We strongly recommend enabling HTTPS for JupyterHub.
We strongly recommend enabling HTTPS for JupyterHub. </p>
</p> {% if login_error %}<p class="login_error">{{ login_error }}</p>{% endif %}
<input type="hidden" name="_xsrf" value="{{ xsrf }}" />
{% if login_error %} <label for="username_input">Username:</label>
<p class="login_error"> <input id="username_input"
{{login_error}} type="text"
</p> autocapitalize="off"
{% endif %} autocorrect="off"
<input type="hidden" name="_xsrf" value="{{ xsrf }}"/> autocomplete="username"
<label for="username_input">Username:</label> class="form-control"
<input name="username"
id="username_input" val="{{ username }}"
type="text" autofocus="autofocus" />
autocapitalize="off" <label for='password_input'>Password:</label>
autocorrect="off" <input type="password"
autocomplete="username" class="form-control"
class="form-control" autocomplete="current-password"
name="username" name="password"
val="{{username}}" id="password_input" />
autofocus="autofocus" {% if authenticator.request_otp %}
/> <label for='otp_input'>{{ authenticator.otp_prompt }}</label>
<label for='password_input'>Password:</label> <input class="form-control"
<input autocomplete="one-time-password"
type="password" name="otp"
class="form-control" id="otp_input" />
autocomplete="current-password" {% endif %}
name="password" <div class="feedback-container">
id="password_input" <input id="login_submit"
/> type="submit"
{% if authenticator.request_otp %} class='btn btn-jupyter form-control'
<label for='otp_input'>{{ authenticator.otp_prompt }}</label> value='Sign in'
<input tabindex="3" />
class="form-control" <div class="feedback-widget hidden">
autocomplete="one-time-password" <i class="fa fa-spinner"></i>
name="otp" </div>
id="otp_input" </div>
/> {% block login_terms %}
{% endif %} {% if login_term_url %}
<div id="login_terms" class="login_terms">
<div class="feedback-container"> <input type="checkbox"
<input id="login_terms_checkbox"
id="login_submit" name="login_terms_checkbox"
type="submit" required />
class='btn btn-jupyter form-control' {% block login_terms_text %}
value='Sign in' {# allow overriding the text #}
tabindex="3" By logging into the platform you accept the <a href="{{ login_term_url }}">terms and conditions</a>.
/> {% endblock login_terms_text %}
<div class="feedback-widget hidden"> </div>
<i class="fa fa-spinner"></i> {% endif %}
</div> {% endblock login_terms %}
</div>
</form>
{% endif %}
{% endblock login_container %}
</div> </div>
{% endblock login %}
{% block login_terms %}
{% if login_term_url %}
<div id="login_terms" class="login_terms">
<input type="checkbox" id="login_terms_checkbox" name="login_terms_checkbox" required />
{% block login_terms_text %} {# allow overriding the text #}
By logging into the platform you accept the <a href="{{ login_term_url }}">terms and conditions</a>.
{% endblock login_terms_text %}
</div>
{% endif %}
{% endblock login_terms %}
</div>
</form>
{% endif %}
{% endblock login_container %}
</div>
{% endblock login %}
{% endblock %} {% endblock %}
{% block script %} {% block script %}
{{ super() }} {{ super() }}
<script> <script>
if (window.location.protocol === "http:") { if (window.location.protocol === "http:") {
// unhide http warning // unhide http warning
var warning = document.getElementById('insecure-login-warning'); var warning = document.getElementById('insecure-login-warning');
@@ -120,5 +102,5 @@ $('form').submit((e) => {
form.find('.feedback-container>*').toggleClass('hidden'); form.find('.feedback-container>*').toggleClass('hidden');
form.find('.feedback-widget>*').toggleClass('fa-pulse'); form.find('.feedback-widget>*').toggleClass('fa-pulse');
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -2,13 +2,8 @@
{% if announcement_logout is string %} {% if announcement_logout is string %}
{% set announcement = announcement_logout %} {% set announcement = announcement_logout %}
{% endif %} {% endif %}
{% block main %} {% block main %}
<div id="logout-main" class="container">
<div id="logout-main" class="container"> <p>Successfully logged out.</p>
<p> </div>
Successfully logged out.
</p>
</div>
{% endblock %} {% endblock %}

View File

@@ -1,60 +1,62 @@
{% extends "page.html" %} {% extends "page.html" %}
{% block main %} {% block main %}
<div class="container">
<div class="container"> <div class="row">
<div class="row"> <div class="text-center">
<div class="text-center"> {% block heading %}
{% block heading %} <h1>
<h1> {% if failed %}
{% if failed %} Spawn failed
Spawn failed {% else %}
{% else %} Server not running
Server not running {% endif %}
{% endif %} </h1>
</h1> {% endblock %}
{% endblock %} {% block message %}
{% block message %} <p>
<p> {% if failed %}
{% if failed %} The latest attempt to start your server {{ server_name }} has failed.
The latest attempt to start your server {{ server_name }} has failed. {% if failed_html_message %}
{% if failed_html_message %} </p>
</p><p>{{ failed_html_message | safe }}</p><p> <p>{{ failed_html_message | safe }}</p>
{% elif failed_message %} <p>{% elif failed_message %}</p>
</p><p>{{ failed_message }}</p><p> <p>{{ failed_message }}</p>
{% endif %} <p>
Would you like to retry starting it? {% endif %}
{% else %} Would you like to retry starting it?
Your server {{ server_name }} is not running. {% else %}
{% if implicit_spawn_seconds %} Your server {{ server_name }} is not running.
It will be restarted automatically. {% if implicit_spawn_seconds %}
If you are not redirected in a few seconds, It will be restarted automatically.
click below to launch your server. If you are not redirected in a few seconds,
{% else %} click below to launch your server.
Would you like to start it? {% else %}
{% endif %} Would you like to start it?
{% endif %} {% endif %}
</p> {% endif %}
{% endblock %} </p>
{% block start_button %} {% endblock %}
<a id="start" role="button" class="btn btn-lg btn-primary" href="{{ spawn_url }}"> {% block start_button %}
{% if failed %} <a id="start"
Relaunch role="button"
{% else %} class="btn btn-lg btn-primary"
Launch href="{{ spawn_url }}">
{% endif %} {% if failed %}
Server {{ server_name }} Relaunch
</a> {% else %}
{% endblock %} Launch
{% endif %}
Server {{ server_name }}
</a>
{% endblock %}
</div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
{{ super () }} {{ super () }}
{% if implicit_spawn_seconds %} {% if implicit_spawn_seconds %}
<script type="text/javascript"> <script type="text/javascript">
var spawn_url = "{{ spawn_url }}"; var spawn_url = "{{ spawn_url }}";
var implicit_spawn_seconds = {{ implicit_spawn_seconds }}; var implicit_spawn_seconds = {{ implicit_spawn_seconds }};
setTimeout(function () { setTimeout(function () {
@@ -63,9 +65,7 @@
}, },
1000 * implicit_spawn_seconds 1000 * implicit_spawn_seconds
); );
</script> </script>
{% endif %} {% endif %}
<script type="text/javascript"> <script type="text/javascript">require(["not_running"]);</script>
require(["not_running"]);
</script>
{% endblock script %} {% endblock script %}

View File

@@ -1,60 +1,47 @@
{% extends "page.html" %} {% extends "page.html" %}
{% block login_widget %}{% endblock %}
{% block login_widget %}
{% endblock %}
{% block main %} {% block main %}
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-8"> <div class="col-md-8">
<h1 class="text-center">Authorize access</h1> <h1 class="text-center">Authorize access</h1>
<p class="lead"> <p class="lead">An application is requesting authorization to access data associated with your JupyterHub account</p>
An application is requesting authorization to access data associated with your JupyterHub account <p>
</p> {{ oauth_client.description }} (oauth URL: {{ oauth_client.redirect_uri }})
<p> would like permission to identify you.
{{ oauth_client.description }} (oauth URL: {{ oauth_client.redirect_uri }}) {% if scope_descriptions | length == 1 and not scope_descriptions[0].scope %}
would like permission to identify you. It will not be able to take actions on
{% if scope_descriptions | length == 1 and not scope_descriptions[0].scope %} your behalf.
It will not be able to take actions on {% endif %}
your behalf. </p>
{% endif %} <form method="POST" action="">
</p> <div class="card">
<div class="card-header">
<form method="POST" action=""> <p class="h5">This will grant the application permission to:</p>
<div class="card">
<div class="card-header">
<p class="h5">This will grant the application permission to:
</p>
</div>
<div class="card-body">
<input type="hidden" name="_xsrf" value="{{ xsrf }}"/>
{# these are the 'real' inputs to the form -#}
{% for scope in allowed_scopes %}
<input type="hidden" name="scopes" value="{{ scope }}"/>
{% endfor %}
{% for scope_info in scope_descriptions %}
<div class="checkbox input-group">
<label>
<input type="checkbox" name="raw-scopes" checked="true" title="This authorization is required"
disabled="disabled"
{# disabled because it's required #} />
<span>
{{ scope_info['description'] }}
{% if scope_info['filter'] %}
Applies to {{ scope_info['filter'] }}.
{% endif %}
</span>
</label>
</div> </div>
{% endfor %} <div class="card-body">
</div> <input type="hidden" name="_xsrf" value="{{ xsrf }}" />
<div class="card-footer"> {# these are the 'real' inputs to the form -#}
<button type="submit" class="form-control btn btn-jupyter mt-2">Authorize</button> {% for scope in allowed_scopes %}<input type="hidden" name="scopes" value="{{ scope }}" />{% endfor %}
{% for scope_info in scope_descriptions %}
<div class="checkbox input-group">
<label>
<input type="checkbox" name="raw-scopes" checked="true" title="This authorization is required"
disabled="disabled"
{# disabled because it's required #} />
<span>
{{ scope_info['description'] }}
{% if scope_info['filter'] %}Applies to {{ scope_info['filter'] }}.{% endif %}
</span>
</label>
</div>
{% endfor %}
</div>
<div class="card-footer">
<button type="submit" class="form-control btn btn-jupyter mt-2">Authorize</button>
</div>
</div>
</form>
</div> </div>
</div> </div>
</form> {% endblock %}
</div>
</div>
{% endblock %}

View File

@@ -1,51 +1,55 @@
{% macro modal(title, btn_label=None, btn_class="btn-primary") %} {% macro modal(title, btn_label=None, btn_class="btn-primary") %}
{% set key = title.replace(' ', '-').lower() %} {% set key = title.replace(' ', '-').lower() %}
{% set btn_label = btn_label or title %} {% set btn_label = btn_label or title %}
<div class="modal fade" id="{{key}}-dialog" tabindex="-1" role="dialog" aria-labelledby="{{key}}-label" aria-hidden="true"> <div class="modal fade"
<div class="modal-dialog"> id="{{ key }}-dialog"
<div class="modal-content"> tabindex="-1"
<div class="modal-header"> role="dialog"
<h2 class="modal-title" id="{{key}}-label">{{title}}</h1> aria-labelledby="{{ key }}-label"
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> aria-hidden="true">
</div> <div class="modal-dialog">
<div class="modal-body"> <div class="modal-content">
{{ caller() }} <div class="modal-header">
</div> <h2 class="modal-title" id="{{ key }}-label">
<div class="modal-footer"> {{ title }}
<button type="button" class="btn {{btn_class}}" data-bs-dismiss="modal" data-dismiss="modal">{{btn_label}}</button> </h1>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body">{{ caller() }}</div>
<div class="modal-footer">
<button type="button"
class="btn {{ btn_class }}"
data-bs-dismiss="modal"
data-dismiss="modal">{{ btn_label }}</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
{% endmacro %} {% endmacro %}
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en"> <html lang="en">
<head>
<head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>
<title>{% block title %}JupyterHub{% endblock %}</title> {% block title %}JupyterHub{% endblock %}
</title>
<meta http-equiv="X-UA-Compatible" content="chrome=1"> <meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
{% block stylesheet %} {% block stylesheet %}
<link rel="stylesheet" href="{{ static_url("css/style.min.css") }}" type="text/css"/> <link rel="stylesheet" href="{{ static_url("css/style.min.css") }}" type="text/css" />
{% endblock %}
{% block favicon %}
<link rel="icon" href="{{ static_url("favicon.ico") }}" type="image/x-icon">
{% endblock %} {% endblock %}
{% block favicon %}<link rel="icon" href="{{ static_url("favicon.ico") }}" type="image/x-icon">{% endblock %}
{% block scripts %} {% block scripts %}
<script src="{{static_url("components/bootstrap/dist/js/bootstrap.bundle.min.js") }}" type="text/javascript" charset="utf-8"></script> <script src="{{static_url("components/bootstrap/dist/js/bootstrap.bundle.min.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script> <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{static_url("components/jquery/dist/jquery.min.js") }}" type="text/javascript" charset="utf-8"></script> <script src="{{static_url("components/jquery/dist/jquery.min.js") }}" type="text/javascript" charset="utf-8"></script>
{% endblock %} {% endblock %}
<script> <script>
require.config({ require.config({
{% if version_hash %} {% if version_hash %}urlArgs: "v={{version_hash}}",{% endif %}
urlArgs: "v={{version_hash}}",
{% endif %}
baseUrl: '{{static_url("js", include_version=False)}}', baseUrl: '{{static_url("js", include_version=False)}}',
paths: { paths: {
components: '../components', components: '../components',
@@ -54,7 +58,6 @@
}, },
}); });
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
window.jhdata = { window.jhdata = {
base_url: "{{base_url}}", base_url: "{{base_url}}",
@@ -79,109 +82,96 @@
{% endif %} {% endif %}
xsrf_token: "{{ xsrf_token }}", xsrf_token: "{{ xsrf_token }}",
} }
</script> </script>
{% block meta %}
{% block meta %}
{% endblock %}
</head>
<body>
<noscript>
<div id='noscript'>
JupyterHub requires JavaScript.<br>
Please enable it to proceed.
</div>
</noscript>
{% block nav_bar %}
<nav class="navbar navbar-expand-sm bg-body-tertiary mb-4">
<div class="container-fluid">
{% block logo %}
<span id="jupyterhub-logo" class="navbar-brand">
<a href="{{logo_url or base_url}}"><img src='{{base_url}}logo' alt='JupyterHub logo' class='jpy-logo' title='Home'/></a>
</span>
{% endblock %}
{% if user %}
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#thenavbar" aria-controls="thenavbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
{% endif %}
<div class="collapse navbar-collapse" id="thenavbar">
{% if user %}
<ul class="navbar-nav me-auto mb-0">
{% block nav_bar_left_items %}
<li class="nav-item"><a class="nav-link" href="{{base_url}}home">Home</a></li>
<li class="nav-item"><a class="nav-link" href="{{base_url}}token">Token</a></li>
{% if 'admin-ui' in parsed_scopes %}
<li class="nav-item"><a class="nav-link" href="{{base_url}}admin">Admin</a></li>
{% endif %}
{% if services %}
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-expanded="false">Services</a>
<ul class="dropdown-menu">
{% for service in services %}
{% block service scoped %}
<li><a class="dropdown-item" href="{{service.href}}">{{service.name}}</a></li>
{% endblock %}
{% endfor %}
</ul>
</li>
{% endif %}
{% endblock %}
</ul>
{% endif %}
<ul class="nav navbar-nav me-2">
{% block nav_bar_right_items %}
<li class="nav-item">
{% block login_widget %}
<span id="login_widget">
{% if user %}
<span class="navbar-text">{{user.name}}</span>
<a id="logout" role="button" class="btn btn-sm btn-outline-dark" href="{{logout_url}}"> <i aria-hidden="true" class="fa fa-sign-out"></i> Logout</a>
{% else %}
<a id="login" role="button" class="btn btn-sm btn-outline-dark" href="{{login_url}}">Login</a>
{% endif %}
</span>
{% endblock %}
</li>
{% endblock %}
</ul>
</div>
{% block header %}
{% endblock %}
</div>
</nav>
{% endblock %} {% endblock %}
</head>
<body>
{% block announcement %} <noscript>
{% if announcement %} <div id='noscript'>
<div class="container text-center announcement alert alert-warning"> JupyterHub requires JavaScript.
{{ announcement | safe }} <br>
Please enable it to proceed.
</div> </div>
</noscript>
{% block nav_bar %}
<nav class="navbar navbar-expand-sm bg-body-tertiary mb-4">
<div class="container-fluid">
{% block logo %}
<span id="jupyterhub-logo" class="navbar-brand">
<a href="{{ logo_url or base_url }}">
<img src='{{ base_url }}logo' alt='JupyterHub logo' class='jpy-logo' title='Home' />
</a>
</span>
{% endblock %}
{% if user %}
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#thenavbar" aria-controls="thenavbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
{% endif %}
<div class="collapse navbar-collapse" id="thenavbar">
{% if user %}
<ul class="navbar-nav me-auto mb-0">
{% block nav_bar_left_items %}
<li class="nav-item">
<a class="nav-link" href="{{ base_url }}home">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ base_url }}token">Token</a>
</li>
{% if 'admin-ui' in parsed_scopes %}
<li class="nav-item">
<a class="nav-link" href="{{ base_url }}admin">Admin</a>
</li>
{% endif %}
{% if services %}
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-expanded="false">Services</a>
<ul class="dropdown-menu">
{% for service in services %}
{% block service scoped %}
<li>
<a class="dropdown-item" href="{{ service.href }}">{{ service.name }}</a>
</li>
{% endblock %}
{% endfor %}
</ul>
</li>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
</ul>
{% endif %}
{% block main %} <ul class="nav navbar-nav me-2">
{% block nav_bar_right_items %}
<li class="nav-item">
{% block login_widget %}
<span id="login_widget">
{% if user %}
<span class="navbar-text">{{ user.name }}</span>
<a id="logout" role="button" class="btn btn-sm btn-outline-dark" href="{{ logout_url }}"> <i aria-hidden="true" class="fa fa-sign-out"></i> Logout</a>
{% else %}
<a id="login" role="button" class="btn btn-sm btn-outline-dark" href="{{ login_url }}">Login</a>
{% endif %}
</span>
{% endblock %} {% endblock %}
</li>
{% block footer %}
{% endblock %} {% endblock %}
</ul>
</div>
{% block header %}{% endblock %}
</div>
</nav>
{% endblock %}
{% block announcement %}
{% if announcement %}
<div class="container text-center announcement alert alert-warning">{{ announcement | safe }}</div>
{% endif %}
{% endblock %}
{% block main %}{% endblock %}
{% block footer %}{% endblock %}
{% call modal('Error', btn_label='OK') %} {% call modal('Error', btn_label='OK') %}
<div class="ajax-error alert-danger"> <div class="ajax-error alert-danger">The error</div>
The error
</div>
{% endcall %} {% endcall %}
{% block script %}{% endblock %}
{% block script %}
{% endblock %}
</body> </body>
</html> </html>

View File

@@ -2,43 +2,39 @@
{% if announcement_spawn is string %} {% if announcement_spawn is string %}
{% set announcement = announcement_spawn %} {% set announcement = announcement_spawn %}
{% endif %} {% endif %}
{% block main %} {% block main %}
<div class="container">
<div class="container"> {% block heading %}
{% block heading %} <div class="row text-center">
<div class="row text-center"> <h1>Server Options</h1>
<h1>Server Options</h1>
</div>
{% endblock %}
<div class="row justify-content-center">
<div class="col-md-8">
{% if for_user and user.name != for_user.name -%}
<p>Spawning server for {{ for_user.name }}</p>
{% endif -%}
{% if error_message -%}
<p class="spawn-error-msg alert alert-danger">
Error: {{error_message}}
</p>
{% endif %}
<form enctype="multipart/form-data" id="spawn_form" action="{{ url | safe }}" method="post" role="form">
{{spawner_options_form | safe}}
<br>
<div class="feedback-container">
<button type="submit" class="btn btn-jupyter form-control">Start</button>
<div class="feedback-widget hidden">
<i class="fa fa-spinner"></i>
</div>
</div> </div>
</form> {% endblock %}
</div> <div class="row justify-content-center">
</div> <div class="col-md-8">
{% if for_user and user.name != for_user.name -%}
{% endblock %} <p>Spawning server for {{ for_user.name }}</p>
{% endif -%}
{% block script %} {% if error_message -%}<p class="spawn-error-msg alert alert-danger">Error: {{ error_message }}</p>{% endif %}
{{ super() }} <form enctype="multipart/form-data"
<script> id="spawn_form"
action="{{ url | safe }}"
method="post"
role="form">
{{ spawner_options_form | safe }}
<br>
<div class="feedback-container">
<button type="submit" class="btn btn-jupyter form-control">Start</button>
<div class="feedback-widget hidden">
<i class="fa fa-spinner"></i>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
{% block script %}
{{ super() }}
<script>
// setup onSubmit feedback // setup onSubmit feedback
$('form').submit((e) => { $('form').submit((e) => {
var form = $(e.target); var form = $(e.target);
@@ -46,5 +42,5 @@ $('form').submit((e) => {
form.find('.feedback-container>*').toggleClass('hidden'); form.find('.feedback-container>*').toggleClass('hidden');
form.find('.feedback-widget>*').toggleClass('fa-pulse'); form.find('.feedback-widget>*').toggleClass('fa-pulse');
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -1,37 +1,39 @@
{% extends "page.html" %} {% extends "page.html" %}
{% block main %} {% block main %}
<div class="container">
<div class="container"> <div class="row">
<div class="row"> <div class="text-center">
<div class="text-center"> {% block message %}
{% block message %} <p>Your server is starting up.</p>
<p>Your server is starting up.</p> <p>You will be redirected automatically when it's ready for you.</p>
<p>You will be redirected automatically when it's ready for you.</p> {% endblock %}
{% endblock %} <div class="progress">
<div class="progress"> <div id="progress-bar"
<div id="progress-bar" class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"> class="progress-bar"
<span class="sr-only"><span id="sr-progress">0%</span> Complete</span> role="progressbar"
aria-valuenow="0"
aria-valuemin="0"
aria-valuemax="100"
style="width: 0%">
<span class="sr-only"><span id="sr-progress">0%</span> Complete</span>
</div>
</div> </div>
<p id="progress-message"></p>
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-8">
<details id="progress-details">
<summary>Event log</summary>
<div id="progress-log"></div>
</details>
</div> </div>
<p id="progress-message"></p>
</div> </div>
</div> </div>
<div class="row justify-content-center">
<div class="col-md-8">
<details id="progress-details">
<summary>Event log</summary>
<div id="progress-log"></div>
</details>
</div>
</div>
</div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
{{ super() }} {{ super() }}
<script type="text/javascript"> <script type="text/javascript">
require(["jquery"], function ($) { require(["jquery"], function ($) {
$("#refresh").click(function () { $("#refresh").click(function () {
window.location.reload(); window.location.reload();
@@ -90,5 +92,5 @@ require(["jquery"], function ($) {
// signal that page has finished loading (mostly for tests) // signal that page has finished loading (mostly for tests)
window._jupyterhub_page_loaded = true; window._jupyterhub_page_loaded = true;
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -1,25 +1,23 @@
{% extends "page.html" %} {% extends "page.html" %}
{% block main %} {% block main %}
<div class="container">
<div class="container"> <div class="row">
<div class="row"> <div class="text-center">
<div class="text-center"> {% block message %}
{% block message %} <p>Your server is stopping.</p>
<p>Your server is stopping.</p> <p>You will be able to start it again once it has finished stopping.</p>
<p>You will be able to start it again once it has finished stopping.</p> {% endblock message %}
{% endblock message %} <p>
<p><i class="fa fa-spinner fa-pulse fa-fw fa-3x" aria-hidden="true"></i></p> <i class="fa fa-spinner fa-pulse fa-fw fa-3x" aria-hidden="true"></i>
<a role="button" id="refresh" class="btn btn-lg btn-primary" href="#">refresh</a> </p>
<a role="button" id="refresh" class="btn btn-lg btn-primary" href="#">refresh</a>
</div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
{{ super() }} {{ super() }}
<script type="text/javascript"> <script type="text/javascript">
require(["jquery"], function ($) { require(["jquery"], function ($) {
$("#refresh").click(function () { $("#refresh").click(function () {
window.location.reload(); window.location.reload();
@@ -28,5 +26,5 @@ require(["jquery"], function ($) {
window.location.reload(); window.location.reload();
}, 5000); }, 5000);
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -1,58 +1,47 @@
{% extends "page.html" %} {% extends "page.html" %}
{% block main %} {% block main %}
<div class="container">
<div class="container"> <h1 class="sr-only">Manage JupyterHub Tokens</h1>
<h1 class="sr-only">Manage JupyterHub Tokens</h1> <div class="row justify-content-center">
<div class="row justify-content-center"> <form id="request-token-form" class="col-lg-6">
<form id="request-token-form" class="col-lg-6"> <div class="form-group">
<div class="form-group"> <label for="token-note" class="form-label">Note</label>
<label for="token-note" class="form-label">Note</label> <input id="token-note"
<input class="form-control"
id="token-note" placeholder="note to identify your new token">
class="form-control" <small id="note-note" class="form-text">This note will help you keep track of what your tokens are for.</small>
placeholder="note to identify your new token"> <br />
<small id="note-note" class="form-text"> <label for="token-expiration-seconds" class="form-label">Token expires in</label>
This note will help you keep track of what your tokens are for. {% block expiration_options %}
</small> <select id="token-expiration-seconds" class="form-select">
<br/> <!-- unit used for each value is `seconds` -->
<label for="token-expiration-seconds" class="form-label">Token expires in</label> <option value="3600">1 Hour</option>
{% block expiration_options %} <option value="86400">1 Day</option>
<select id="token-expiration-seconds" <option value="604800">1 Week</option>
class="form-select"> <option value="" selected="selected">Never</option>
<!-- unit used for each value is `seconds` --> </select>
<option value="3600">1 Hour</option> {% endblock expiration_options %}
<option value="86400">1 Day</option> <small id="note-expires-at" class="form-text">You can configure when your token will expire.</small>
<option value="604800">1 Week</option> <br />
<option value="" selected="selected">Never</option> <label for="token-scopes" class="form-label">Permissions</label>
</select> <input id="token-scopes"
{% endblock expiration_options %} class="form-control"
<small id="note-expires-at" class="form-text"> placeholder="list of scopes for the token to have, separated by space">
You can configure when your token will expire. <small id="note-token-scopes" class="form-text">
</small> You can limit the permissions of the token so it can only do what you want it to.
<br/> If none are specified, the token will have permission to do everything you can do.
<label for="token-scopes" class="form-label">Permissions</label> See the <a href="https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes">JupyterHub documentation for a list of available scopes</a>.
<input id="token-scopes" class="form-control" placeholder="list of scopes for the token to have, separated by space"> </small>
<small id="note-token-scopes" class="form-text"> </div>
You can limit the permissions of the token so it can only do what you want it to. <div class="text-center m-4">
If none are specified, the token will have permission to do everything you can do. <button type="submit" class="btn btn-lg btn-jupyter">Request new API token</button>
See the <a href="https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes">JupyterHub documentation for a list of available scopes</a>. </div>
</small> </form>
</div> </div>
<div class="text-center m-4"> <div class="row justify-content-center">
<button type="submit" class="btn btn-lg btn-jupyter">
Request new API token
</button>
</div>
</form>
</div>
<div class="row justify-content-center">
<div id="token-area" class="col-lg-6" style="display: none;"> <div id="token-area" class="col-lg-6" style="display: none;">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">Your new API Token</div>
Your new API Token
</div>
<div class="card-body"> <div class="card-body">
<p class="card-title text-center"> <p class="card-title text-center">
<span id="token-result"></span> <span id="token-result"></span>
@@ -64,138 +53,129 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% if api_tokens %}
{% if api_tokens %} <div class="row" id="api-tokens-section">
<div class="row" id="api-tokens-section"> <div class="col">
<div class="col"> <h2>API Tokens</h2>
<h2>API Tokens</h2> <p>
<p> These are tokens with access to the JupyterHub API.
These are tokens with access to the JupyterHub API. Permissions for each token may be viewed via the JupyterHub tokens API.
Permissions for each token may be viewed via the JupyterHub tokens API. Revoking the API token for a running server will require restarting that server.
Revoking the API token for a running server will require restarting that server. </p>
</p> <table class="table table-striped" id="api-tokens-table">
<table class="table table-striped" id="api-tokens-table"> <thead>
<thead> <tr>
<tr> <th>Note</th>
<th>Note</th> <th>Permissions</th>
<th>Permissions</th> <th>Last used</th>
<th>Last used</th> <th>Created</th>
<th>Created</th> <th>Expires</th>
<th>Expires</th> </tr>
</tr> </thead>
</thead> <tbody>
<tbody> {% for token in api_tokens %}
{% for token in api_tokens %} <tr class="token-row container" data-token-id="{{ token.api_id }}">
<tr class="token-row container" data-token-id="{{token.api_id}}"> {% block token_row scoped %}
{% block token_row scoped %} <td class="note-col col">{{ token.note }}</td>
<td class="note-col col">{{token.note}}</td> <td class="scope-col col">
<td class="scope-col col"> <details>
<details> <summary>scopes</summary>
<summary>scopes</summary> {% for scope in token.scopes %}<pre class="token-scope">{{ scope }}</pre>{% endfor %}
{% for scope in token.scopes %} </details>
<pre class="token-scope">{{ scope }}</pre> </td>
{% endfor %} <td class="time-col col">
</details> {%- if token.last_activity -%}
</td> {{ token.last_activity.isoformat() + 'Z' }}
<td class="time-col col"> {%- else -%}
{%- if token.last_activity -%} Never
{{ token.last_activity.isoformat() + 'Z' }} {%- endif -%}
{%- else -%} </td>
Never <td class="time-col col">
{%- endif -%} {%- if token.created -%}
</td> {{ token.created.isoformat() + 'Z' }}
<td class="time-col col"> {%- else -%}
{%- if token.created -%} N/A
{{ token.created.isoformat() + 'Z' }} {%- endif -%}
{%- else -%} </td>
N/A <td class="time-col col">
{%- endif -%} {%- if token.expires_at -%}
</td> {{ token.expires_at.isoformat() + 'Z' }}
<td class="time-col col"> {%- else -%}
{%- if token.expires_at -%} Never
{{ token.expires_at.isoformat() + 'Z' }} {%- endif -%}
{%- else -%} </td>
Never <td class="col text-center">
{%- endif -%} <button class="revoke-token-btn btn btn-xs btn-danger">revoke</button>
</td> </td>
<td class="col text-center"> {% endblock token_row %}
<button class="revoke-token-btn btn btn-xs btn-danger">revoke</button> </tr>
</td> {% endfor %}
{% endblock token_row %} </tbody>
</tr> </table>
{% endfor %} </div>
</tbody> </div>
</table> {% endif %}
</div> {% if oauth_clients %}
</div> <div class="row" id="oauth-clients-section">
{% endif %} <h2>Authorized Applications</h2>
<p>
{% if oauth_clients %} These are applications that use OAuth with JupyterHub
<div class="row" id="oauth-clients-section"> to identify users (mostly notebook servers).
<h2>Authorized Applications</h2> OAuth tokens can generally only be used to identify you,
<p> not take actions on your behalf.
These are applications that use OAuth with JupyterHub </p>
to identify users (mostly notebook servers). <table class="table table-striped" id="oauth-tokens-table">
<thead>
OAuth tokens can generally only be used to identify you, <tr>
not take actions on your behalf. <th>Application</th>
</p> <th>Permissions</th>
<table class="table table-striped" id="oauth-tokens-table"> <th>Last used</th>
<thead> <th>First authorized</th>
<tr> </tr>
<th>Application</th> </thead>
<th>Permissions</th> <tbody>
<th>Last used</th> {% for client in oauth_clients %}
<th>First authorized</th> <tr class="token-row" data-token-id="{{ client['token_id'] }}">
</tr> {% block client_row scoped %}
</thead> <td class="note-col col-sm-4">{{ client['description'] }}</td>
<tbody> <td class="scope-col col-sm-1">
{% for client in oauth_clients %} <details>
<tr class="token-row" <summary>scopes</summary>
data-token-id="{{ client['token_id'] }}"> {# create set of scopes on all tokens -#}
{% block client_row scoped %} {# sum concatenates all token.scopes into a single list -#}
<td class="note-col col-sm-4">{{ client['description'] }}</td> {# then filter to unique set and sort -#}
<td class="scope-col col-sm-1"> {% for scope in client.tokens | sum(attribute="scopes", start=[]) | unique | sort %}
<details> <pre class="token-scope">{{ scope }}</pre>
<summary>scopes</summary> {% endfor %}
{# create set of scopes on all tokens -#} </details>
{# sum concatenates all token.scopes into a single list -#} </td>
{# then filter to unique set and sort -#} <td class="time-col col-sm-3">
{% for scope in client.tokens | sum(attribute="scopes", start=[]) | unique | sort %} {%- if client['last_activity'] -%}
<pre class="token-scope">{{ scope }}</pre> {{ client['last_activity'].isoformat() + 'Z' }}
{% endfor %} {%- else -%}
</details> Never
</td> {%- endif -%}
<td class="time-col col-sm-3"> </td>
{%- if client['last_activity'] -%} <td class="time-col col-sm-3">
{{ client['last_activity'].isoformat() + 'Z' }} {%- if client['created'] -%}
{%- else -%} {{ client['created'].isoformat() + 'Z' }}
Never {%- else -%}
{%- endif -%} N/A
</td> {%- endif -%}
<td class="time-col col-sm-3"> </td>
{%- if client['created'] -%} <td class="col-sm-1 text-center">
{{ client['created'].isoformat() + 'Z' }} <button class="revoke-token-btn btn btn-xs btn-danger">revoke</button>
{%- else -%} {% endblock client_row %}
N/A </tr>
{%- endif -%} {% endfor %}
</td> </tbody>
<td class="col-sm-1 text-center"> </table>
<button class="revoke-token-btn btn btn-xs btn-danger">revoke</button> </div>
{% endblock client_row %} {% endif %}
</tr> </div>
{% endfor %} {% endblock main %}
</tbody> {% block script %}
</table> {{ super() }}
</div> <script type="text/javascript">require(["token"]);</script>
{% endif %} {% endblock script %}
</div>
{% endblock main %}
{% block script %}
{{ super() }}
<script type="text/javascript">
require(["token"]);
</script>
{% endblock script %}