mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-07 01:54:09 +00:00
add and run djlint formatter
This commit is contained in:
@@ -33,6 +33,15 @@ repos:
|
||||
rev: v4.0.0-alpha.8
|
||||
hooks:
|
||||
- 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
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
|
@@ -1,26 +1,24 @@
|
||||
{%- extends "!layout.html" %}
|
||||
|
||||
{# not sure why, but theme CSS prevents scrolling within redoc content
|
||||
# If this were fixed, we could keep the navbar and footer
|
||||
#}
|
||||
{%- block css %}{%- endblock %}
|
||||
{%- block docs_navbar %}{%- endblock %}
|
||||
{%- block footer %}{% endblock %}
|
||||
{%- block body_tag %}
|
||||
<body>
|
||||
{%- endblock %}
|
||||
|
||||
{% block css %}
|
||||
{% endblock css %}
|
||||
{% block docs_navbar %}
|
||||
{% endblock docs_navbar %}
|
||||
{% block footer %}
|
||||
{% endblock footer %}
|
||||
{# djlint: off #}
|
||||
{%- block body_tag -%}<body>{%- endblock body_tag %}
|
||||
{# djlint: on #}
|
||||
{%- block extrahead %}
|
||||
{{ super() }}
|
||||
<link href="{{ pathto('_static/redoc-fonts.css', 1) }}"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script src="{{ pathto('_static/redoc.js', 1) }}"></script>
|
||||
{%- endblock %}
|
||||
|
||||
{{ super() }}
|
||||
<link href="{{ pathto('_static/redoc-fonts.css', 1) }}" rel="stylesheet" />
|
||||
<script src="{{ pathto('_static/redoc.js', 1) }}"></script>
|
||||
{%- endblock extrahead %}
|
||||
{%- block content %}
|
||||
<redoc id="redoc-spec"></redoc>
|
||||
<script>
|
||||
<redoc id="redoc-spec"></redoc>
|
||||
<script>
|
||||
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."
|
||||
} else {
|
||||
@@ -30,5 +28,5 @@
|
||||
document.getElementById("redoc-spec"),
|
||||
);
|
||||
}
|
||||
</script>
|
||||
{%- endblock %}
|
||||
</script>
|
||||
{%- endblock content %}
|
||||
|
@@ -1,9 +1,10 @@
|
||||
{% extends "templates/page.html" %} {% block announcement %}
|
||||
<div class="container text-center announcement"></div>
|
||||
{% endblock %} {% block script %} {{ super() }}
|
||||
<script>
|
||||
{% extends "templates/page.html" %}
|
||||
{% block announcement %}<div class="container text-center announcement"></div>{% endblock %}
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
$.get("/services/announcement/", function (data) {
|
||||
$(".announcement").html(data["announcement"]);
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@@ -2,11 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<title>JupyterHub</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<link rel="stylesheet" href="/static/css/style.min.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@@ -7,30 +7,23 @@ It makes the following modifications:
|
||||
- update logo url to jupyterhub
|
||||
- remove `?redirects` url param that may be added by jupyterhub
|
||||
#}
|
||||
|
||||
{% extends "templates/page.html" %}
|
||||
|
||||
{% block header_buttons %}
|
||||
{{ super() }}
|
||||
<span>
|
||||
<a
|
||||
href="{{hub_control_panel_url}}"
|
||||
id="jupyterhub-control-panel-link"
|
||||
class="btn btn-default btn-sm navbar-btn pull-right"
|
||||
style="margin-right: 4px; margin-left: 2px"
|
||||
>
|
||||
Control Panel
|
||||
</a>
|
||||
</span>
|
||||
{{ super() }}
|
||||
<span>
|
||||
<a href="{{ hub_control_panel_url }}"
|
||||
id="jupyterhub-control-panel-link"
|
||||
class="btn btn-default btn-sm navbar-btn pull-right"
|
||||
style="margin-right: 4px;
|
||||
margin-left: 2px">Control Panel</a>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block logo %}
|
||||
<img src="{{logo_url}}" alt="Jupyter Notebook" />
|
||||
<img src="{{ logo_url }}" alt="Jupyter Notebook" />
|
||||
{% endblock logo %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
function _remove_redirects_param() {
|
||||
// remove ?redirects= param from URL so that
|
||||
// successful page loads don't increment the redirect loop counter
|
||||
@@ -57,5 +50,5 @@ It makes the following modifications:
|
||||
}
|
||||
}
|
||||
_remove_redirects_param();
|
||||
</script>
|
||||
</script>
|
||||
{% endblock script %}
|
||||
|
@@ -120,6 +120,11 @@ select = [
|
||||
"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
|
||||
# the version, making a git commit and tag, and pushing changes.
|
||||
#
|
||||
|
@@ -1,5 +1,2 @@
|
||||
{% 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 %}
|
||||
|
@@ -1,55 +1,51 @@
|
||||
{% extends "page.html" %}
|
||||
{% block login_widget %}
|
||||
{% endblock %}
|
||||
|
||||
{% block login_widget %}{% endblock %}
|
||||
{% block main %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h1 class="text-center">Accept sharing invitation</h1>
|
||||
<p class="lead">
|
||||
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>
|
||||
</p>
|
||||
|
||||
{% if not spawner_ready %}
|
||||
<p class="alert alert-danger">
|
||||
The server at {{ spawner_url }} is not currently running.
|
||||
After accepting permission, you may need to ask {{ owner.name }}
|
||||
to start the server before you can access it.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
By accepting the invitation, you will be granted the following permissions,
|
||||
restricted to this particular server:
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{# these are the 'real' inputs to the form -#}
|
||||
<input type="hidden" name="_xsrf" value="{{ xsrf }}" />
|
||||
<input type="hidden" name="code" value="{{ code }}" />
|
||||
{% for scope_info in scope_descriptions -%}
|
||||
<div class="form-check input-group">
|
||||
<label>
|
||||
<span>
|
||||
{{ scope_info['description'] }}
|
||||
{% if scope_info['filter'] %}
|
||||
Applies to {{ scope_info['filter'] }}. {% endif %}
|
||||
</span>
|
||||
</label>
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<h1 class="text-center">Accept sharing invitation</h1>
|
||||
<p class="lead">
|
||||
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>
|
||||
</p>
|
||||
{% if not spawner_ready %}
|
||||
<p class="alert alert-danger">
|
||||
The server at {{ spawner_url }} is not currently running.
|
||||
After accepting permission, you may need to ask {{ owner.name }}
|
||||
to start the server before you can access it.
|
||||
</p>
|
||||
{% endif %}
|
||||
<form method="POST" action="">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
By accepting the invitation, you will be granted the following permissions,
|
||||
restricted to this particular server:
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{# these are the 'real' inputs to the form -#}
|
||||
<input type="hidden" name="_xsrf" value="{{ xsrf }}" />
|
||||
<input type="hidden" name="code" value="{{ code }}" />
|
||||
{% for scope_info in scope_descriptions -%}
|
||||
<div class="form-check input-group">
|
||||
<label>
|
||||
<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">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>
|
||||
{% 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>
|
||||
</form>
|
||||
</div></div></div>
|
||||
|
||||
{% endblock %}
|
||||
|
@@ -1,17 +1,13 @@
|
||||
{% extends "page.html" %}
|
||||
|
||||
{% block main %}
|
||||
<div id="react-admin-hook">
|
||||
<script id="jupyterhub-admin-config">
|
||||
<div id="react-admin-hook">
|
||||
<script id="jupyterhub-admin-config">
|
||||
window.api_page_limit = parseInt("{{ api_page_limit|safe }}")
|
||||
window.base_url = "{{ base_url|safe }}"
|
||||
</script>
|
||||
<script src={{ static_url("js/admin-react.js") }}></script>
|
||||
</div>
|
||||
</script>
|
||||
<script src={{ static_url("js/admin-react.js") }}></script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<div class="py-2 px-4 bg-body-tertiary small version_footer">
|
||||
JupyterHub {{ server_version }}
|
||||
</div>
|
||||
<div class="py-2 px-4 bg-body-tertiary small version_footer">JupyterHub {{ server_version }}</div>
|
||||
{% endblock %}
|
||||
|
@@ -1,40 +1,19 @@
|
||||
{% extends "page.html" %}
|
||||
|
||||
{% block login_widget %}
|
||||
{% endblock %}
|
||||
|
||||
{% block login_widget %}{% endblock %}
|
||||
{% block main %}
|
||||
|
||||
<div class="error">
|
||||
{% block h1_error %}
|
||||
<h1>
|
||||
{{status_code}} : {{status_message}}
|
||||
</h1>
|
||||
{% endblock h1_error %}
|
||||
{% block error_detail %}
|
||||
{% if message %}
|
||||
<p>
|
||||
{{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>
|
||||
|
||||
<div class="error">
|
||||
{% block h1_error %}
|
||||
<h1>{{ status_code }} : {{ status_message }}</h1>
|
||||
{% endblock h1_error %}
|
||||
{% block error_detail %}
|
||||
{% if message %}<p>{{ 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 %}
|
||||
|
||||
{% block script %}
|
||||
{{super()}}
|
||||
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
function _remove_redirects_from_url() {
|
||||
if (window.location.search.length <= 1) {
|
||||
|
@@ -2,93 +2,89 @@
|
||||
{% if announcement_home is string %}
|
||||
{% set announcement = announcement_home %}
|
||||
{% endif %}
|
||||
|
||||
{% block main %}
|
||||
<div class="container">
|
||||
<h1 class="sr-only">JupyterHub home page</h1>
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
{% if default_server.active %}
|
||||
<a id="stop" role="button" class="btn btn-lg btn-danger">
|
||||
Stop My Server
|
||||
<div class="container">
|
||||
<h1 class="sr-only">JupyterHub home page</h1>
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
{% if default_server.active %}<a id="stop" role="button" class="btn btn-lg btn-danger">Stop My Server</a>{% endif %}
|
||||
<a id="start"
|
||||
role="button"
|
||||
class="btn btn-lg btn-primary"
|
||||
href="{{ url }}">
|
||||
{% if not default_server.active %}Start{% endif %}
|
||||
My Server
|
||||
</a>
|
||||
{% endif %}
|
||||
<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>
|
||||
{% 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 %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
require(["home"]);
|
||||
</script>
|
||||
{{ super() }}
|
||||
<script type="text/javascript">require(["home"]);</script>
|
||||
{% endblock %}
|
||||
|
@@ -2,112 +2,94 @@
|
||||
{% if announcement_login is string %}
|
||||
{% set announcement = announcement_login %}
|
||||
{% endif %}
|
||||
|
||||
{% block login_widget %}
|
||||
{% endblock %}
|
||||
|
||||
{% block login_widget %}{% endblock %}
|
||||
{% block main %}
|
||||
|
||||
{% block login %}
|
||||
<div id="login-main" class="container">
|
||||
{% block login_container %}
|
||||
{% if custom_html %}
|
||||
{{ custom_html | safe }}
|
||||
{% elif login_service %}
|
||||
<div class="service-login">
|
||||
<p id='insecure-login-warning' class='hidden'>
|
||||
Warning: JupyterHub seems to be served over an unsecured HTTP connection.
|
||||
We strongly recommend enabling HTTPS for JupyterHub.
|
||||
</p>
|
||||
|
||||
<a role="button" class='btn btn-jupyter btn-lg' href='{{ authenticator_login_url | safe }}'>
|
||||
Sign in with {{login_service}}
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<form action="{{ authenticator_login_url | safe }}" method="post" role="form">
|
||||
<div class="auth-form-header">
|
||||
<h1>Sign in</h1>
|
||||
</div>
|
||||
<div class='auth-form-body m-auto'>
|
||||
|
||||
<p id='insecure-login-warning' class='hidden'>
|
||||
Warning: JupyterHub seems to be served over an unsecured HTTP connection.
|
||||
We strongly recommend enabling HTTPS for JupyterHub.
|
||||
</p>
|
||||
|
||||
{% if login_error %}
|
||||
<p class="login_error">
|
||||
{{login_error}}
|
||||
</p>
|
||||
{% endif %}
|
||||
<input type="hidden" name="_xsrf" value="{{ xsrf }}"/>
|
||||
<label for="username_input">Username:</label>
|
||||
<input
|
||||
id="username_input"
|
||||
type="text"
|
||||
autocapitalize="off"
|
||||
autocorrect="off"
|
||||
autocomplete="username"
|
||||
class="form-control"
|
||||
name="username"
|
||||
val="{{username}}"
|
||||
autofocus="autofocus"
|
||||
/>
|
||||
<label for='password_input'>Password:</label>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
autocomplete="current-password"
|
||||
name="password"
|
||||
id="password_input"
|
||||
/>
|
||||
{% if authenticator.request_otp %}
|
||||
<label for='otp_input'>{{ authenticator.otp_prompt }}</label>
|
||||
<input
|
||||
class="form-control"
|
||||
autocomplete="one-time-password"
|
||||
name="otp"
|
||||
id="otp_input"
|
||||
/>
|
||||
{% endif %}
|
||||
|
||||
<div class="feedback-container">
|
||||
<input
|
||||
id="login_submit"
|
||||
type="submit"
|
||||
class='btn btn-jupyter form-control'
|
||||
value='Sign in'
|
||||
tabindex="3"
|
||||
/>
|
||||
<div class="feedback-widget hidden">
|
||||
<i class="fa fa-spinner"></i>
|
||||
</div>
|
||||
{% block login %}
|
||||
<div id="login-main" class="container">
|
||||
{% block login_container %}
|
||||
{% if custom_html %}
|
||||
{{ custom_html | safe }}
|
||||
{% elif login_service %}
|
||||
<div class="service-login">
|
||||
<p id='insecure-login-warning' class='hidden'>
|
||||
Warning: JupyterHub seems to be served over an unsecured HTTP connection.
|
||||
We strongly recommend enabling HTTPS for JupyterHub.
|
||||
</p>
|
||||
<a role="button"
|
||||
class='btn btn-jupyter btn-lg'
|
||||
href='{{ authenticator_login_url | safe }}'>Sign in with {{ login_service }}</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<form action="{{ authenticator_login_url | safe }}"
|
||||
method="post"
|
||||
role="form">
|
||||
<div class="auth-form-header">
|
||||
<h1>Sign in</h1>
|
||||
</div>
|
||||
<div class='auth-form-body m-auto'>
|
||||
<p id='insecure-login-warning' class='hidden'>
|
||||
Warning: JupyterHub seems to be served over an unsecured HTTP connection.
|
||||
We strongly recommend enabling HTTPS for JupyterHub.
|
||||
</p>
|
||||
{% if login_error %}<p class="login_error">{{ login_error }}</p>{% endif %}
|
||||
<input type="hidden" name="_xsrf" value="{{ xsrf }}" />
|
||||
<label for="username_input">Username:</label>
|
||||
<input id="username_input"
|
||||
type="text"
|
||||
autocapitalize="off"
|
||||
autocorrect="off"
|
||||
autocomplete="username"
|
||||
class="form-control"
|
||||
name="username"
|
||||
val="{{ username }}"
|
||||
autofocus="autofocus" />
|
||||
<label for='password_input'>Password:</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
autocomplete="current-password"
|
||||
name="password"
|
||||
id="password_input" />
|
||||
{% if authenticator.request_otp %}
|
||||
<label for='otp_input'>{{ authenticator.otp_prompt }}</label>
|
||||
<input class="form-control"
|
||||
autocomplete="one-time-password"
|
||||
name="otp"
|
||||
id="otp_input" />
|
||||
{% endif %}
|
||||
<div class="feedback-container">
|
||||
<input id="login_submit"
|
||||
type="submit"
|
||||
class='btn btn-jupyter form-control'
|
||||
value='Sign in'
|
||||
tabindex="3" />
|
||||
<div class="feedback-widget hidden">
|
||||
<i class="fa fa-spinner"></i>
|
||||
</div>
|
||||
</div>
|
||||
{% 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>
|
||||
|
||||
{% 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 login %}
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
{{ super() }}
|
||||
<script>
|
||||
if (window.location.protocol === "http:") {
|
||||
// unhide http warning
|
||||
var warning = document.getElementById('insecure-login-warning');
|
||||
@@ -120,5 +102,5 @@ $('form').submit((e) => {
|
||||
form.find('.feedback-container>*').toggleClass('hidden');
|
||||
form.find('.feedback-widget>*').toggleClass('fa-pulse');
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@@ -2,13 +2,8 @@
|
||||
{% if announcement_logout is string %}
|
||||
{% set announcement = announcement_logout %}
|
||||
{% endif %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div id="logout-main" class="container">
|
||||
<p>
|
||||
Successfully logged out.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="logout-main" class="container">
|
||||
<p>Successfully logged out.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@@ -1,60 +1,62 @@
|
||||
{% extends "page.html" %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
{% block heading %}
|
||||
<h1>
|
||||
{% if failed %}
|
||||
Spawn failed
|
||||
{% else %}
|
||||
Server not running
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% endblock %}
|
||||
{% block message %}
|
||||
<p>
|
||||
{% if failed %}
|
||||
The latest attempt to start your server {{ server_name }} has failed.
|
||||
{% if failed_html_message %}
|
||||
</p><p>{{ failed_html_message | safe }}</p><p>
|
||||
{% elif failed_message %}
|
||||
</p><p>{{ failed_message }}</p><p>
|
||||
{% endif %}
|
||||
Would you like to retry starting it?
|
||||
{% else %}
|
||||
Your server {{ server_name }} is not running.
|
||||
{% if implicit_spawn_seconds %}
|
||||
It will be restarted automatically.
|
||||
If you are not redirected in a few seconds,
|
||||
click below to launch your server.
|
||||
{% else %}
|
||||
Would you like to start it?
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endblock %}
|
||||
{% block start_button %}
|
||||
<a id="start" role="button" class="btn btn-lg btn-primary" href="{{ spawn_url }}">
|
||||
{% if failed %}
|
||||
Relaunch
|
||||
{% else %}
|
||||
Launch
|
||||
{% endif %}
|
||||
Server {{ server_name }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
{% block heading %}
|
||||
<h1>
|
||||
{% if failed %}
|
||||
Spawn failed
|
||||
{% else %}
|
||||
Server not running
|
||||
{% endif %}
|
||||
</h1>
|
||||
{% endblock %}
|
||||
{% block message %}
|
||||
<p>
|
||||
{% if failed %}
|
||||
The latest attempt to start your server {{ server_name }} has failed.
|
||||
{% if failed_html_message %}
|
||||
</p>
|
||||
<p>{{ failed_html_message | safe }}</p>
|
||||
<p>{% elif failed_message %}</p>
|
||||
<p>{{ failed_message }}</p>
|
||||
<p>
|
||||
{% endif %}
|
||||
Would you like to retry starting it?
|
||||
{% else %}
|
||||
Your server {{ server_name }} is not running.
|
||||
{% if implicit_spawn_seconds %}
|
||||
It will be restarted automatically.
|
||||
If you are not redirected in a few seconds,
|
||||
click below to launch your server.
|
||||
{% else %}
|
||||
Would you like to start it?
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endblock %}
|
||||
{% block start_button %}
|
||||
<a id="start"
|
||||
role="button"
|
||||
class="btn btn-lg btn-primary"
|
||||
href="{{ spawn_url }}">
|
||||
{% if failed %}
|
||||
Relaunch
|
||||
{% else %}
|
||||
Launch
|
||||
{% endif %}
|
||||
Server {{ server_name }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block script %}
|
||||
{{ super () }}
|
||||
{% if implicit_spawn_seconds %}
|
||||
<script type="text/javascript">
|
||||
{{ super () }}
|
||||
{% if implicit_spawn_seconds %}
|
||||
<script type="text/javascript">
|
||||
var spawn_url = "{{ spawn_url }}";
|
||||
var implicit_spawn_seconds = {{ implicit_spawn_seconds }};
|
||||
setTimeout(function () {
|
||||
@@ -63,9 +65,7 @@
|
||||
},
|
||||
1000 * implicit_spawn_seconds
|
||||
);
|
||||
</script>
|
||||
{% endif %}
|
||||
<script type="text/javascript">
|
||||
require(["not_running"]);
|
||||
</script>
|
||||
</script>
|
||||
{% endif %}
|
||||
<script type="text/javascript">require(["not_running"]);</script>
|
||||
{% endblock script %}
|
||||
|
@@ -1,60 +1,47 @@
|
||||
{% extends "page.html" %}
|
||||
|
||||
{% block login_widget %}
|
||||
{% endblock %}
|
||||
|
||||
{% block login_widget %}{% endblock %}
|
||||
{% block main %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<h1 class="text-center">Authorize access</h1>
|
||||
<p class="lead">
|
||||
An application is requesting authorization to access data associated with your JupyterHub account
|
||||
</p>
|
||||
<p>
|
||||
{{ oauth_client.description }} (oauth URL: {{ oauth_client.redirect_uri }})
|
||||
would like permission to identify you.
|
||||
{% if scope_descriptions | length == 1 and not scope_descriptions[0].scope %}
|
||||
It will not be able to take actions on
|
||||
your behalf.
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
<form method="POST" action="">
|
||||
<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 class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<h1 class="text-center">Authorize access</h1>
|
||||
<p class="lead">An application is requesting authorization to access data associated with your JupyterHub account</p>
|
||||
<p>
|
||||
{{ oauth_client.description }} (oauth URL: {{ oauth_client.redirect_uri }})
|
||||
would like permission to identify you.
|
||||
{% if scope_descriptions | length == 1 and not scope_descriptions[0].scope %}
|
||||
It will not be able to take actions on
|
||||
your behalf.
|
||||
{% endif %}
|
||||
</p>
|
||||
<form method="POST" action="">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<p class="h5">This will grant the application permission to:</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="form-control btn btn-jupyter mt-2">Authorize</button>
|
||||
<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>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="form-control btn btn-jupyter mt-2">Authorize</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
@@ -1,51 +1,55 @@
|
||||
{% macro modal(title, btn_label=None, btn_class="btn-primary") %}
|
||||
{% set key = title.replace(' ', '-').lower() %}
|
||||
{% 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-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="{{key}}-label">{{title}}</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>
|
||||
{% set key = title.replace(' ', '-').lower() %}
|
||||
{% 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-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title" id="{{ key }}-label">
|
||||
{{ title }}
|
||||
</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>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title>{% block title %}JupyterHub{% endblock %}</title>
|
||||
<title>
|
||||
{% block title %}JupyterHub{% endblock %}
|
||||
</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
{% block stylesheet %}
|
||||
<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">
|
||||
<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 %}
|
||||
{% 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/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/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/jquery/dist/jquery.min.js") }}" type="text/javascript" charset="utf-8"></script>
|
||||
{% endblock %}
|
||||
<script>
|
||||
require.config({
|
||||
{% if version_hash %}
|
||||
urlArgs: "v={{version_hash}}",
|
||||
{% endif %}
|
||||
{% if version_hash %}urlArgs: "v={{version_hash}}",{% endif %}
|
||||
baseUrl: '{{static_url("js", include_version=False)}}',
|
||||
paths: {
|
||||
components: '../components',
|
||||
@@ -54,7 +58,6 @@
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.jhdata = {
|
||||
base_url: "{{base_url}}",
|
||||
@@ -79,109 +82,96 @@
|
||||
{% endif %}
|
||||
xsrf_token: "{{ xsrf_token }}",
|
||||
}
|
||||
</script>
|
||||
|
||||
{% 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>
|
||||
</script>
|
||||
{% block meta %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block announcement %}
|
||||
{% if announcement %}
|
||||
<div class="container text-center announcement alert alert-warning">
|
||||
{{ announcement | safe }}
|
||||
</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 %}
|
||||
|
||||
|
||||
{% block main %}
|
||||
</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 %}
|
||||
|
||||
{% block footer %}
|
||||
</li>
|
||||
{% 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') %}
|
||||
<div class="ajax-error alert-danger">
|
||||
The error
|
||||
</div>
|
||||
<div class="ajax-error alert-danger">The error</div>
|
||||
{% endcall %}
|
||||
|
||||
{% block script %}
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}{% endblock %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@@ -2,43 +2,39 @@
|
||||
{% if announcement_spawn is string %}
|
||||
{% set announcement = announcement_spawn %}
|
||||
{% endif %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="container">
|
||||
{% block heading %}
|
||||
<div class="row text-center">
|
||||
<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 class="container">
|
||||
{% block heading %}
|
||||
<div class="row text-center">
|
||||
<h1>Server Options</h1>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
{% 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>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
// setup onSubmit feedback
|
||||
$('form').submit((e) => {
|
||||
var form = $(e.target);
|
||||
@@ -46,5 +42,5 @@ $('form').submit((e) => {
|
||||
form.find('.feedback-container>*').toggleClass('hidden');
|
||||
form.find('.feedback-widget>*').toggleClass('fa-pulse');
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@@ -1,37 +1,39 @@
|
||||
{% extends "page.html" %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
{% block message %}
|
||||
<p>Your server is starting up.</p>
|
||||
<p>You will be redirected automatically when it's ready for you.</p>
|
||||
{% endblock %}
|
||||
<div class="progress">
|
||||
<div id="progress-bar" class="progress-bar" 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 class="container">
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
{% block message %}
|
||||
<p>Your server is starting up.</p>
|
||||
<p>You will be redirected automatically when it's ready for you.</p>
|
||||
{% endblock %}
|
||||
<div class="progress">
|
||||
<div id="progress-bar"
|
||||
class="progress-bar"
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
require(["jquery"], function ($) {
|
||||
$("#refresh").click(function () {
|
||||
window.location.reload();
|
||||
@@ -90,5 +92,5 @@ require(["jquery"], function ($) {
|
||||
// signal that page has finished loading (mostly for tests)
|
||||
window._jupyterhub_page_loaded = true;
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@@ -1,25 +1,23 @@
|
||||
{% extends "page.html" %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
{% block message %}
|
||||
<p>Your server is stopping.</p>
|
||||
<p>You will be able to start it again once it has finished stopping.</p>
|
||||
{% endblock message %}
|
||||
<p><i class="fa fa-spinner fa-pulse fa-fw fa-3x" aria-hidden="true"></i></p>
|
||||
<a role="button" id="refresh" class="btn btn-lg btn-primary" href="#">refresh</a>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="text-center">
|
||||
{% block message %}
|
||||
<p>Your server is stopping.</p>
|
||||
<p>You will be able to start it again once it has finished stopping.</p>
|
||||
{% endblock message %}
|
||||
<p>
|
||||
<i class="fa fa-spinner fa-pulse fa-fw fa-3x" aria-hidden="true"></i>
|
||||
</p>
|
||||
<a role="button" id="refresh" class="btn btn-lg btn-primary" href="#">refresh</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
require(["jquery"], function ($) {
|
||||
$("#refresh").click(function () {
|
||||
window.location.reload();
|
||||
@@ -28,5 +26,5 @@ require(["jquery"], function ($) {
|
||||
window.location.reload();
|
||||
}, 5000);
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@@ -1,58 +1,47 @@
|
||||
{% extends "page.html" %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="container">
|
||||
<h1 class="sr-only">Manage JupyterHub Tokens</h1>
|
||||
<div class="row justify-content-center">
|
||||
<form id="request-token-form" class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label for="token-note" class="form-label">Note</label>
|
||||
<input
|
||||
id="token-note"
|
||||
class="form-control"
|
||||
placeholder="note to identify your new token">
|
||||
<small id="note-note" class="form-text">
|
||||
This note will help you keep track of what your tokens are for.
|
||||
</small>
|
||||
<br/>
|
||||
<label for="token-expiration-seconds" class="form-label">Token expires in</label>
|
||||
{% block expiration_options %}
|
||||
<select id="token-expiration-seconds"
|
||||
class="form-select">
|
||||
<!-- unit used for each value is `seconds` -->
|
||||
<option value="3600">1 Hour</option>
|
||||
<option value="86400">1 Day</option>
|
||||
<option value="604800">1 Week</option>
|
||||
<option value="" selected="selected">Never</option>
|
||||
</select>
|
||||
{% endblock expiration_options %}
|
||||
<small id="note-expires-at" class="form-text">
|
||||
You can configure when your token will expire.
|
||||
</small>
|
||||
<br/>
|
||||
<label for="token-scopes" class="form-label">Permissions</label>
|
||||
<input id="token-scopes" class="form-control" placeholder="list of scopes for the token to have, separated by space">
|
||||
<small id="note-token-scopes" class="form-text">
|
||||
You can limit the permissions of the token so it can only do what you want it to.
|
||||
If none are specified, the token will have permission to do everything you can do.
|
||||
See the <a href="https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes">JupyterHub documentation for a list of available scopes</a>.
|
||||
</small>
|
||||
</div>
|
||||
<div class="text-center m-4">
|
||||
<button type="submit" class="btn btn-lg btn-jupyter">
|
||||
Request new API token
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
<div class="container">
|
||||
<h1 class="sr-only">Manage JupyterHub Tokens</h1>
|
||||
<div class="row justify-content-center">
|
||||
<form id="request-token-form" class="col-lg-6">
|
||||
<div class="form-group">
|
||||
<label for="token-note" class="form-label">Note</label>
|
||||
<input id="token-note"
|
||||
class="form-control"
|
||||
placeholder="note to identify your new token">
|
||||
<small id="note-note" class="form-text">This note will help you keep track of what your tokens are for.</small>
|
||||
<br />
|
||||
<label for="token-expiration-seconds" class="form-label">Token expires in</label>
|
||||
{% block expiration_options %}
|
||||
<select id="token-expiration-seconds" class="form-select">
|
||||
<!-- unit used for each value is `seconds` -->
|
||||
<option value="3600">1 Hour</option>
|
||||
<option value="86400">1 Day</option>
|
||||
<option value="604800">1 Week</option>
|
||||
<option value="" selected="selected">Never</option>
|
||||
</select>
|
||||
{% endblock expiration_options %}
|
||||
<small id="note-expires-at" class="form-text">You can configure when your token will expire.</small>
|
||||
<br />
|
||||
<label for="token-scopes" class="form-label">Permissions</label>
|
||||
<input id="token-scopes"
|
||||
class="form-control"
|
||||
placeholder="list of scopes for the token to have, separated by space">
|
||||
<small id="note-token-scopes" class="form-text">
|
||||
You can limit the permissions of the token so it can only do what you want it to.
|
||||
If none are specified, the token will have permission to do everything you can do.
|
||||
See the <a href="https://jupyterhub.readthedocs.io/en/stable/rbac/scopes.html#available-scopes">JupyterHub documentation for a list of available scopes</a>.
|
||||
</small>
|
||||
</div>
|
||||
<div class="text-center m-4">
|
||||
<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 class="card">
|
||||
<div class="card-header">
|
||||
Your new API Token
|
||||
</div>
|
||||
<div class="card-header">Your new API Token</div>
|
||||
<div class="card-body">
|
||||
<p class="card-title text-center">
|
||||
<span id="token-result"></span>
|
||||
@@ -64,138 +53,129 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if api_tokens %}
|
||||
<div class="row" id="api-tokens-section">
|
||||
<div class="col">
|
||||
<h2>API Tokens</h2>
|
||||
<p>
|
||||
These are tokens with access to the JupyterHub 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.
|
||||
</p>
|
||||
<table class="table table-striped" id="api-tokens-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Note</th>
|
||||
<th>Permissions</th>
|
||||
<th>Last used</th>
|
||||
<th>Created</th>
|
||||
<th>Expires</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for token in api_tokens %}
|
||||
<tr class="token-row container" data-token-id="{{token.api_id}}">
|
||||
{% block token_row scoped %}
|
||||
<td class="note-col col">{{token.note}}</td>
|
||||
<td class="scope-col col">
|
||||
<details>
|
||||
<summary>scopes</summary>
|
||||
{% for scope in token.scopes %}
|
||||
<pre class="token-scope">{{ scope }}</pre>
|
||||
{% endfor %}
|
||||
</details>
|
||||
</td>
|
||||
<td class="time-col col">
|
||||
{%- if token.last_activity -%}
|
||||
{{ token.last_activity.isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
Never
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="time-col col">
|
||||
{%- if token.created -%}
|
||||
{{ token.created.isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
N/A
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="time-col col">
|
||||
{%- if token.expires_at -%}
|
||||
{{ token.expires_at.isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
Never
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="col text-center">
|
||||
<button class="revoke-token-btn btn btn-xs btn-danger">revoke</button>
|
||||
</td>
|
||||
{% endblock token_row %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if oauth_clients %}
|
||||
<div class="row" id="oauth-clients-section">
|
||||
<h2>Authorized Applications</h2>
|
||||
<p>
|
||||
These are applications that use OAuth with JupyterHub
|
||||
to identify users (mostly notebook servers).
|
||||
|
||||
OAuth tokens can generally only be used to identify you,
|
||||
not take actions on your behalf.
|
||||
</p>
|
||||
<table class="table table-striped" id="oauth-tokens-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Application</th>
|
||||
<th>Permissions</th>
|
||||
<th>Last used</th>
|
||||
<th>First authorized</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for client in oauth_clients %}
|
||||
<tr class="token-row"
|
||||
data-token-id="{{ client['token_id'] }}">
|
||||
{% block client_row scoped %}
|
||||
<td class="note-col col-sm-4">{{ client['description'] }}</td>
|
||||
<td class="scope-col col-sm-1">
|
||||
<details>
|
||||
<summary>scopes</summary>
|
||||
{# create set of scopes on all tokens -#}
|
||||
{# sum concatenates all token.scopes into a single list -#}
|
||||
{# then filter to unique set and sort -#}
|
||||
{% for scope in client.tokens | sum(attribute="scopes", start=[]) | unique | sort %}
|
||||
<pre class="token-scope">{{ scope }}</pre>
|
||||
{% endfor %}
|
||||
</details>
|
||||
</td>
|
||||
<td class="time-col col-sm-3">
|
||||
{%- if client['last_activity'] -%}
|
||||
{{ client['last_activity'].isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
Never
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="time-col col-sm-3">
|
||||
{%- if client['created'] -%}
|
||||
{{ client['created'].isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
N/A
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="col-sm-1 text-center">
|
||||
<button class="revoke-token-btn btn btn-xs btn-danger">revoke</button>
|
||||
{% endblock client_row %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock main %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
require(["token"]);
|
||||
</script>
|
||||
{% endblock script %}
|
||||
</div>
|
||||
{% if api_tokens %}
|
||||
<div class="row" id="api-tokens-section">
|
||||
<div class="col">
|
||||
<h2>API Tokens</h2>
|
||||
<p>
|
||||
These are tokens with access to the JupyterHub 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.
|
||||
</p>
|
||||
<table class="table table-striped" id="api-tokens-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Note</th>
|
||||
<th>Permissions</th>
|
||||
<th>Last used</th>
|
||||
<th>Created</th>
|
||||
<th>Expires</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for token in api_tokens %}
|
||||
<tr class="token-row container" data-token-id="{{ token.api_id }}">
|
||||
{% block token_row scoped %}
|
||||
<td class="note-col col">{{ token.note }}</td>
|
||||
<td class="scope-col col">
|
||||
<details>
|
||||
<summary>scopes</summary>
|
||||
{% for scope in token.scopes %}<pre class="token-scope">{{ scope }}</pre>{% endfor %}
|
||||
</details>
|
||||
</td>
|
||||
<td class="time-col col">
|
||||
{%- if token.last_activity -%}
|
||||
{{ token.last_activity.isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
Never
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="time-col col">
|
||||
{%- if token.created -%}
|
||||
{{ token.created.isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
N/A
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="time-col col">
|
||||
{%- if token.expires_at -%}
|
||||
{{ token.expires_at.isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
Never
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="col text-center">
|
||||
<button class="revoke-token-btn btn btn-xs btn-danger">revoke</button>
|
||||
</td>
|
||||
{% endblock token_row %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if oauth_clients %}
|
||||
<div class="row" id="oauth-clients-section">
|
||||
<h2>Authorized Applications</h2>
|
||||
<p>
|
||||
These are applications that use OAuth with JupyterHub
|
||||
to identify users (mostly notebook servers).
|
||||
OAuth tokens can generally only be used to identify you,
|
||||
not take actions on your behalf.
|
||||
</p>
|
||||
<table class="table table-striped" id="oauth-tokens-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Application</th>
|
||||
<th>Permissions</th>
|
||||
<th>Last used</th>
|
||||
<th>First authorized</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for client in oauth_clients %}
|
||||
<tr class="token-row" data-token-id="{{ client['token_id'] }}">
|
||||
{% block client_row scoped %}
|
||||
<td class="note-col col-sm-4">{{ client['description'] }}</td>
|
||||
<td class="scope-col col-sm-1">
|
||||
<details>
|
||||
<summary>scopes</summary>
|
||||
{# create set of scopes on all tokens -#}
|
||||
{# sum concatenates all token.scopes into a single list -#}
|
||||
{# then filter to unique set and sort -#}
|
||||
{% for scope in client.tokens | sum(attribute="scopes", start=[]) | unique | sort %}
|
||||
<pre class="token-scope">{{ scope }}</pre>
|
||||
{% endfor %}
|
||||
</details>
|
||||
</td>
|
||||
<td class="time-col col-sm-3">
|
||||
{%- if client['last_activity'] -%}
|
||||
{{ client['last_activity'].isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
Never
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="time-col col-sm-3">
|
||||
{%- if client['created'] -%}
|
||||
{{ client['created'].isoformat() + 'Z' }}
|
||||
{%- else -%}
|
||||
N/A
|
||||
{%- endif -%}
|
||||
</td>
|
||||
<td class="col-sm-1 text-center">
|
||||
<button class="revoke-token-btn btn btn-xs btn-danger">revoke</button>
|
||||
{% endblock client_row %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock main %}
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">require(["token"]);</script>
|
||||
{% endblock script %}
|
||||
|
Reference in New Issue
Block a user