init
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
.python-version
|
||||||
|
.idea
|
||||||
|
media/*
|
||||||
|
utils/delme.py
|
||||||
|
*.pyc
|
||||||
|
.env
|
||||||
|
/venv/
|
||||||
9
Dockerfile
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
FROM debian:12
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
WORKDIR /app
|
||||||
|
RUN date
|
||||||
|
RUN apt update
|
||||||
|
RUN apt install gnupg2 netcat-traditional libssl-dev libcrypto++-dev lsb-release python3-pip -y
|
||||||
|
COPY requirements.txt /app/requirements.txt
|
||||||
|
RUN pip3 install --break-system-packages -r requirements.txt
|
||||||
|
#ENTRYPOINT ["./run.sh"]
|
||||||
0
apps/__init__.py
Normal file
0
apps/api/__init__.py
Normal file
3
apps/api/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
apps/api/apps.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ApiConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.api'
|
||||||
0
apps/api/migrations/__init__.py
Normal file
19
apps/api/models.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
from django.db import models
|
||||||
|
from oauth2_provider.models import get_application_model
|
||||||
|
|
||||||
|
from apps.users.models import User
|
||||||
|
|
||||||
|
Application = get_application_model()
|
||||||
|
|
||||||
|
|
||||||
|
class Invoke(models.Model):
|
||||||
|
caller_application = models.ForeignKey(to=Application, on_delete=models.PROTECT)
|
||||||
|
callee_application = models.CharField(to=Application, on_delete=models.PROTECT)
|
||||||
|
caller_user = models.ForeignKey(to=User, on_delete=models.PROTECT)
|
||||||
|
cost = models.DecimalField()
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
# TODO: move to external model
|
||||||
|
request = models.JSONField()
|
||||||
|
response = models.JSONField()
|
||||||
|
|
||||||
3
apps/api/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
9
apps/api/urls.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django.urls import path, include
|
||||||
|
from .views import myview
|
||||||
|
app_name = "core"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# path('', HomeView.as_view(), name='home'),
|
||||||
|
path('proxy/(?P<path>.*)', myview),
|
||||||
|
]
|
||||||
|
|
||||||
0
apps/api/utils/__init__.py
Normal file
129
apps/api/utils/views.py
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.http import QueryDict
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
|
||||||
|
def proxy_view(request, url, requests_args=None):
|
||||||
|
"""
|
||||||
|
Forward as close to an exact copy of the request as possible along to the
|
||||||
|
given url. Respond with as close to an exact copy of the resulting
|
||||||
|
response as possible.
|
||||||
|
|
||||||
|
If there are any additional arguments you wish to send to requests, put
|
||||||
|
them in the requests_args dictionary.
|
||||||
|
"""
|
||||||
|
requests_args = (requests_args or {}).copy()
|
||||||
|
headers = get_headers(request.META)
|
||||||
|
params = request.GET.copy()
|
||||||
|
|
||||||
|
if 'headers' not in requests_args:
|
||||||
|
requests_args['headers'] = {}
|
||||||
|
if 'data' not in requests_args:
|
||||||
|
requests_args['data'] = request.body
|
||||||
|
if 'params' not in requests_args:
|
||||||
|
requests_args['params'] = QueryDict('', mutable=True)
|
||||||
|
|
||||||
|
# Overwrite any headers and params from the incoming request with explicitly
|
||||||
|
# specified values for the requests library.
|
||||||
|
headers.update(requests_args['headers'])
|
||||||
|
params.update(requests_args['params'])
|
||||||
|
|
||||||
|
# If there's a content-length header from Django, it's probably in all-caps
|
||||||
|
# and requests might not notice it, so just remove it.
|
||||||
|
for key in list(headers.keys()):
|
||||||
|
if key.lower() == 'content-length':
|
||||||
|
del headers[key]
|
||||||
|
|
||||||
|
requests_args['headers'] = headers
|
||||||
|
requests_args['params'] = params
|
||||||
|
|
||||||
|
response = requests.request(request.method, url, **requests_args)
|
||||||
|
|
||||||
|
proxy_response = HttpResponse(
|
||||||
|
response.content,
|
||||||
|
status=response.status_code)
|
||||||
|
|
||||||
|
excluded_headers = set([
|
||||||
|
# Hop-by-hop headers
|
||||||
|
# ------------------
|
||||||
|
# Certain response headers should NOT be just tunneled through. These
|
||||||
|
# are they. For more info, see:
|
||||||
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
|
||||||
|
'connection', 'keep-alive', 'proxy-authenticate',
|
||||||
|
'proxy-authorization', 'te', 'trailers', 'transfer-encoding',
|
||||||
|
'upgrade',
|
||||||
|
|
||||||
|
# Although content-encoding is not listed among the hop-by-hop headers,
|
||||||
|
# it can cause trouble as well. Just let the server set the value as
|
||||||
|
# it should be.
|
||||||
|
'content-encoding',
|
||||||
|
|
||||||
|
# Since the remote server may or may not have sent the content in the
|
||||||
|
# same encoding as Django will, let Django worry about what the length
|
||||||
|
# should be.
|
||||||
|
'content-length',
|
||||||
|
])
|
||||||
|
|
||||||
|
# Treat Set-Cookie header in a special way, as Django uses a cookie jar to
|
||||||
|
# manage cookies instead of the normal headers dict:
|
||||||
|
# https://docs.djangoproject.com/en/5.0/ref/request-response/#django.http.HttpResponse.headers
|
||||||
|
#
|
||||||
|
# Use headers.get instead of headers.pop in case there are users that rely
|
||||||
|
# on the Set-Cookie header value.
|
||||||
|
cookies_header = response.headers.get('set-cookie', '')
|
||||||
|
proxy_response.cookies.load(cookies_header)
|
||||||
|
|
||||||
|
for key, value in response.headers.items():
|
||||||
|
if key.lower() in excluded_headers:
|
||||||
|
continue
|
||||||
|
elif key.lower() == 'location':
|
||||||
|
# If the location is relative at all, we want it to be absolute to
|
||||||
|
# the upstream server.
|
||||||
|
proxy_response[key] = make_absolute_location(response.url, value)
|
||||||
|
else:
|
||||||
|
proxy_response[key] = value
|
||||||
|
|
||||||
|
return proxy_response
|
||||||
|
|
||||||
|
|
||||||
|
def make_absolute_location(base_url, location):
|
||||||
|
"""
|
||||||
|
Convert a location header into an absolute URL.
|
||||||
|
"""
|
||||||
|
absolute_pattern = re.compile(r'^[a-zA-Z]+://.*$')
|
||||||
|
if absolute_pattern.match(location):
|
||||||
|
return location
|
||||||
|
|
||||||
|
parsed_url = urlparse(base_url)
|
||||||
|
|
||||||
|
if location.startswith('//'):
|
||||||
|
# scheme relative
|
||||||
|
return parsed_url.scheme + ':' + location
|
||||||
|
|
||||||
|
elif location.startswith('/'):
|
||||||
|
# host relative
|
||||||
|
return parsed_url.scheme + '://' + parsed_url.netloc + location
|
||||||
|
|
||||||
|
else:
|
||||||
|
# path relative
|
||||||
|
return parsed_url.scheme + '://' + parsed_url.netloc + parsed_url.path.rsplit('/', 1)[0] + '/' + location
|
||||||
|
|
||||||
|
return location
|
||||||
|
|
||||||
|
|
||||||
|
def get_headers(environ):
|
||||||
|
"""
|
||||||
|
Retrieve the HTTP headers from a WSGI environment dictionary. See
|
||||||
|
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
|
||||||
|
"""
|
||||||
|
headers = {}
|
||||||
|
for key, value in environ.items():
|
||||||
|
# Sometimes, things don't like when you send the requesting host through.
|
||||||
|
if key.startswith('HTTP_') and key != 'HTTP_HOST':
|
||||||
|
headers[key[5:].replace('_', '-')] = value
|
||||||
|
elif key in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
|
||||||
|
headers[key.replace('_', '-')] = value
|
||||||
|
|
||||||
|
return headers
|
||||||
12
apps/api/views.py
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
from .utils.views import proxy_view
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def proxy_view(request, path):
|
||||||
|
extra_requests_args = {...}
|
||||||
|
remoteurl = 'http://<host_name>/' + path
|
||||||
|
return proxy_view(request, remoteurl, extra_requests_args)
|
||||||
|
|
||||||
0
apps/core/__init__.py
Normal file
2
apps/core/admin.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
6
apps/core/apps.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CoreConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.core'
|
||||||
0
apps/core/migrations/__init__.py
Normal file
2
apps/core/models.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
1
apps/core/templates/jalali_date/widgets/attrs.html
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{% for name, value in widget.attrs.items %}{% if value is not False %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}
|
||||||
1
apps/core/templates/jalali_date/widgets/date.html
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<input type="{{ widget.type }}" name="{{ widget.name }}"{% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}{% include "django/forms/widgets/attrs.html" %} data-jdp>
|
||||||
0
apps/core/templatetags/__init__.py
Normal file
30
apps/core/templatetags/tags.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
from django import template
|
||||||
|
import jdatetime
|
||||||
|
from django.core.paginator import Paginator
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name="to_jalali")
|
||||||
|
def to_jalali(value):
|
||||||
|
if value:
|
||||||
|
return jdatetime.date.fromgregorian(date=value, locale='fa_IR').strftime("%Y/%m/%d")
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
@register.filter(name="amount_split_3digit")
|
||||||
|
def amount_split_3digit(value):
|
||||||
|
text = str(value)[::-1]
|
||||||
|
return ','.join([l[::-1] for l in [text[i:i + 3] for i in range(0, len(text), 3)][::-1]])
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def get_proper_elided_page_range(p, number, on_each_side=2, on_ends=2):
|
||||||
|
paginator = Paginator(p.object_list, p.per_page)
|
||||||
|
return paginator.get_elided_page_range(number=number,
|
||||||
|
on_each_side=on_each_side,
|
||||||
|
on_ends=on_ends)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def get_item(dictionary, key):
|
||||||
|
return dictionary.get(key)
|
||||||
3
apps/core/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
7
apps/core/urls.py
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
from django.urls import path, include
|
||||||
|
from .views import HomeView
|
||||||
|
app_name = "core"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', HomeView.as_view(), name='home'),
|
||||||
|
]
|
||||||
0
apps/core/utils/__init__.py
Normal file
51
apps/core/utils/jalali_date.py
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
from django import forms
|
||||||
|
from django.conf import settings
|
||||||
|
from django.templatetags.static import static
|
||||||
|
from django.contrib.admin.widgets import AdminDateWidget
|
||||||
|
from django.forms.fields import DateField, SplitDateTimeField, TimeField, DateTimeField
|
||||||
|
from django.utils.encoding import force_str
|
||||||
|
from datetime import date as datetime_date, datetime as datetime_datetime
|
||||||
|
from jdatetime import GregorianToJalali, datetime as jalali_datetime
|
||||||
|
import jdatetime
|
||||||
|
|
||||||
|
class JalaliDateField(DateField):
|
||||||
|
def __init__(self, *, input_formats=None, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.input_formats.insert(0, settings.JALALI_DATE_DEFAULTS['Strftime']['date'])
|
||||||
|
|
||||||
|
def prepare_value(self, value):
|
||||||
|
if isinstance(value, datetime_date):
|
||||||
|
date_obj = jdatetime.datetime.fromgregorian(datetime=value)
|
||||||
|
return date_obj.strftime(settings.JALALI_DATE_DEFAULTS['Strftime']['date'])
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def strptime(self, value, format):
|
||||||
|
return jalali_datetime.strptime(force_str(value), format).togregorian().date()
|
||||||
|
|
||||||
|
|
||||||
|
class CustomSelectWidget(forms.widgets.Select):
|
||||||
|
option_template_name = 'widgets/select_option.html'
|
||||||
|
|
||||||
|
|
||||||
|
class AdminJalaliDateWidget(AdminDateWidget):
|
||||||
|
template_name = "jalali_date/widgets/date.html"
|
||||||
|
|
||||||
|
# @property
|
||||||
|
# def media(self):
|
||||||
|
# js = settings.JALALI_DATE_DEFAULTS['Static']['js']
|
||||||
|
# css = settings.JALALI_DATE_DEFAULTS['Static']['css']
|
||||||
|
# return forms.Media(js=[static(path) for path in js], css=css)
|
||||||
|
|
||||||
|
def __init__(self, attrs=None, format=None):
|
||||||
|
final_attrs = {'class': 'jalali_date-date', 'size': '10'}
|
||||||
|
if attrs is not None:
|
||||||
|
final_attrs.update(attrs)
|
||||||
|
super(AdminJalaliDateWidget, self).__init__(attrs=final_attrs, format=format)
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, renderer=None):
|
||||||
|
"""Render the widget as an HTML string."""
|
||||||
|
context = self.get_context(name, value, attrs)
|
||||||
|
return self._render(self.template_name, context, renderer)
|
||||||
|
|
||||||
|
|
||||||
10
apps/core/views.py
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
class HomeView(TemplateView):
|
||||||
|
template_name = 'core/home.html'
|
||||||
|
|
||||||
0
apps/gooyal_oauth2/__init__.py
Normal file
13
apps/gooyal_oauth2/admin.py
Executable file
|
|
@ -0,0 +1,13 @@
|
||||||
|
"""
|
||||||
|
Django admin configuration for the gooyal-restrict-scopes package.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.admin.sites import NotRegistered
|
||||||
|
|
||||||
|
from oauth2_provider.admin import ApplicationAdmin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# The restricted application is registered by Django OAuth Toolkit, but we want
|
||||||
|
# to provide our own admin that uses our form
|
||||||
6
apps/gooyal_oauth2/apps.py
Executable file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class GooyalOauthConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.gooyal_oauth2'
|
||||||
9
apps/gooyal_oauth2/forms.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
"""
|
||||||
|
Django forms for use with the gooyal-restrict-scopes package.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from oauth2_provider.scopes import get_scopes_backend
|
||||||
|
|
||||||
|
|
||||||
121
apps/gooyal_oauth2/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-07-04 11:18
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import oauth2_provider.generators
|
||||||
|
import oauth2_provider.models
|
||||||
|
import uuid
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Application',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('client_id', models.CharField(db_index=True, default=oauth2_provider.generators.generate_client_id, max_length=100, unique=True)),
|
||||||
|
('redirect_uris', models.TextField(blank=True, help_text='Allowed URIs list, space separated')),
|
||||||
|
('post_logout_redirect_uris', models.TextField(blank=True, default='', help_text='Allowed Post Logout URIs list, space separated')),
|
||||||
|
('client_type', models.CharField(choices=[('confidential', 'Confidential'), ('public', 'Public')], max_length=32)),
|
||||||
|
('authorization_grant_type', models.CharField(choices=[('authorization-code', 'Authorization code'), ('implicit', 'Implicit'), ('password', 'Resource owner password-based'), ('client-credentials', 'Client credentials'), ('openid-hybrid', 'OpenID connect hybrid')], max_length=32)),
|
||||||
|
('client_secret', oauth2_provider.models.ClientSecretField(blank=True, db_index=True, default=oauth2_provider.generators.generate_client_secret, help_text='Hashed on Save. Copy it now if this is a new secret.', max_length=255)),
|
||||||
|
('hash_client_secret', models.BooleanField(default=True)),
|
||||||
|
('name', models.CharField(blank=True, max_length=255)),
|
||||||
|
('skip_authorization', models.BooleanField(default=False)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('algorithm', models.CharField(blank=True, choices=[('', 'No OIDC support'), ('RS256', 'RSA with SHA-2 256'), ('HS256', 'HMAC with SHA-2 256')], default='', max_length=5)),
|
||||||
|
('allowed_origins', models.TextField(blank=True, default='', help_text='Allowed origins list to enable CORS, space separated')),
|
||||||
|
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Grant',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('code', models.CharField(max_length=255, unique=True)),
|
||||||
|
('expires', models.DateTimeField()),
|
||||||
|
('redirect_uri', models.TextField()),
|
||||||
|
('scope', models.TextField(blank=True)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('code_challenge', models.CharField(blank=True, default='', max_length=128)),
|
||||||
|
('code_challenge_method', models.CharField(blank=True, choices=[('plain', 'plain'), ('S256', 'S256')], default='', max_length=10)),
|
||||||
|
('nonce', models.CharField(blank=True, default='', max_length=255)),
|
||||||
|
('claims', models.TextField(blank=True)),
|
||||||
|
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='IDToken',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('jti', models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name='JWT Token ID')),
|
||||||
|
('expires', models.DateTimeField()),
|
||||||
|
('scope', models.TextField(blank=True)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('application', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
|
||||||
|
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AccessToken',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('token', models.CharField(db_index=True, max_length=255, unique=True)),
|
||||||
|
('expires', models.DateTimeField()),
|
||||||
|
('scope', models.TextField(blank=True)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('detail', models.JSONField(blank=True, null=True)),
|
||||||
|
('client_id', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('client_owner', models.UUIDField(blank=True, null=True)),
|
||||||
|
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('application', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
|
||||||
|
('id_token', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='access_token', to=settings.OAUTH2_PROVIDER_ID_TOKEN_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RefreshToken',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||||
|
('token', models.CharField(max_length=255)),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('revoked', models.DateTimeField(null=True)),
|
||||||
|
('access_token', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='refresh_token', to=settings.OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL)),
|
||||||
|
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='accesstoken',
|
||||||
|
name='source_refresh_token',
|
||||||
|
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='refreshed_access_token', to=settings.OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-07-04 11:38
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('gooyal_oauth2', '0001_initial'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='accesstoken',
|
||||||
|
name='client_owner',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
0
apps/gooyal_oauth2/migrations/__init__.py
Normal file
34
apps/gooyal_oauth2/models.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# models
|
||||||
|
from django.db import models
|
||||||
|
from oauth2_provider.models import AbstractApplication, AbstractAccessToken, AbstractGrant, AbstractRefreshToken, \
|
||||||
|
AbstractIDToken
|
||||||
|
|
||||||
|
|
||||||
|
class AccessToken(AbstractAccessToken):
|
||||||
|
detail = models.JSONField(null=True, blank=True)
|
||||||
|
client_id = models.CharField(max_length=255, null=True, blank=True)
|
||||||
|
client_owner = models.ForeignKey('users.User', on_delete=models.PROTECT, null=True, blank=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = False
|
||||||
|
|
||||||
|
|
||||||
|
class Application(AbstractApplication):
|
||||||
|
class Meta:
|
||||||
|
abstract = False
|
||||||
|
|
||||||
|
|
||||||
|
class Grant(AbstractGrant):
|
||||||
|
class Meta:
|
||||||
|
abstract = False
|
||||||
|
|
||||||
|
|
||||||
|
class RefreshToken(AbstractRefreshToken):
|
||||||
|
class Meta:
|
||||||
|
abstract = False
|
||||||
|
|
||||||
|
|
||||||
|
class IDToken(AbstractIDToken):
|
||||||
|
class Meta:
|
||||||
|
abstract = False
|
||||||
|
|
||||||
19
apps/gooyal_oauth2/rest_framework.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from oauth2_provider.contrib.rest_framework import TokenMatchesOASRequirements, OAuth2Authentication
|
||||||
|
from rest_framework.permissions import (
|
||||||
|
IsAuthenticated
|
||||||
|
)
|
||||||
|
|
||||||
|
log = logging.getLogger("oauth2_provider")
|
||||||
|
|
||||||
|
|
||||||
|
class IsAuthenticatedOrTokenMatchesOASRequirements(TokenMatchesOASRequirements):
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
is_authenticated = IsAuthenticated().has_permission(request, view)
|
||||||
|
oauth2authenticated = False
|
||||||
|
if is_authenticated:
|
||||||
|
oauth2authenticated = isinstance(request.successful_authenticator, OAuth2Authentication)
|
||||||
|
|
||||||
|
token_has_scope = TokenMatchesOASRequirements()
|
||||||
|
return (is_authenticated and not oauth2authenticated) or token_has_scope.has_permission(request, view)
|
||||||
164
apps/gooyal_oauth2/validators.py
Executable file
|
|
@ -0,0 +1,164 @@
|
||||||
|
# import service_clients
|
||||||
|
import base64
|
||||||
|
import http.client
|
||||||
|
import logging
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.utils.timezone import make_aware
|
||||||
|
from oauth2_provider.models import (
|
||||||
|
get_access_token_model,
|
||||||
|
get_application_model,
|
||||||
|
get_grant_model,
|
||||||
|
get_id_token_model,
|
||||||
|
get_refresh_token_model,
|
||||||
|
)
|
||||||
|
from oauth2_provider.oauth2_validators import OAuth2Validator as BaseOAuth2Validator
|
||||||
|
from oauth2_provider.settings import oauth2_settings
|
||||||
|
from oauth2_provider.utils import get_timezone
|
||||||
|
|
||||||
|
Application = get_application_model()
|
||||||
|
AccessToken = get_access_token_model()
|
||||||
|
IDToken = get_id_token_model()
|
||||||
|
Grant = get_grant_model()
|
||||||
|
RefreshToken = get_refresh_token_model()
|
||||||
|
UserModel = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger("oauth2_provider")
|
||||||
|
|
||||||
|
|
||||||
|
class OAuth2Validator(BaseOAuth2Validator): # pylint: disable=w0223
|
||||||
|
def get_or_create_user_from_content(self, content):
|
||||||
|
"""
|
||||||
|
An optional layer to define where to store the profile in `UserModel` or a separate model.
|
||||||
|
For example `UserOAuth`, where `user = models.OneToOneField(UserModel)` .
|
||||||
|
|
||||||
|
The function is called after checking that username is in the content.
|
||||||
|
|
||||||
|
Returns an UserModel instance;
|
||||||
|
"""
|
||||||
|
user, _ = UserModel.objects.get_or_create(pk=content["username"])
|
||||||
|
return user
|
||||||
|
|
||||||
|
def _get_token_from_authentication_server(
|
||||||
|
self, token, introspection_url, introspection_token, introspection_credentials
|
||||||
|
):
|
||||||
|
# NOTICE: onlu change from orginal method is that we create application here
|
||||||
|
"""Use external introspection endpoint to "crack open" the token.
|
||||||
|
:param introspection_url: introspection endpoint URL
|
||||||
|
:param introspection_token: Bearer token
|
||||||
|
:param introspection_credentials: Basic Auth credentials (id,secret)
|
||||||
|
:return: :class:`models.AccessToken`
|
||||||
|
|
||||||
|
Some RFC 7662 implementations (including this one) use a Bearer token while others use Basic
|
||||||
|
Auth. Depending on the external AS's implementation, provide either the introspection_token
|
||||||
|
or the introspection_credentials.
|
||||||
|
|
||||||
|
If the resulting access_token identifies a username (e.g. Authorization Code grant), add
|
||||||
|
that user to the UserModel. Also cache the access_token up until its expiry time or a
|
||||||
|
configured maximum time.
|
||||||
|
|
||||||
|
"""
|
||||||
|
headers = None
|
||||||
|
if introspection_token:
|
||||||
|
headers = {"Authorization": "Bearer {}".format(introspection_token)}
|
||||||
|
elif introspection_credentials:
|
||||||
|
client_id = introspection_credentials[0].encode("utf-8")
|
||||||
|
client_secret = introspection_credentials[1].encode("utf-8")
|
||||||
|
basic_auth = base64.b64encode(client_id + b":" + client_secret)
|
||||||
|
headers = {"Authorization": "Basic {}".format(basic_auth.decode("utf-8"))}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(introspection_url, data={"token": token}, headers=headers)
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
log.exception("Introspection: Failed POST to %r in token lookup", introspection_url)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Log an exception when response from auth server is not successful
|
||||||
|
if response.status_code != http.client.OK:
|
||||||
|
log.exception(
|
||||||
|
"Introspection: Failed to get a valid response "
|
||||||
|
"from authentication server. Status code: {}, "
|
||||||
|
"Reason: {}.".format(response.status_code, response.reason)
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
content = response.json()
|
||||||
|
except ValueError:
|
||||||
|
log.exception("Introspection: Failed to parse response as json")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if "active" in content and content["active"] is True:
|
||||||
|
try:
|
||||||
|
application_introspection_url = introspection_url.rstrip("/") + "_application/"
|
||||||
|
response = requests.post(application_introspection_url, data={"client_id": content['client_id']}, headers=headers)
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
log.exception("Introspection: Failed POST to %r in token lookup", introspection_url)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if response.status_code != http.client.OK:
|
||||||
|
log.exception(
|
||||||
|
"Application introspection: Failed to get a valid response "
|
||||||
|
"from authentication server. Status code: {}, "
|
||||||
|
"Reason: {}.".format(response.status_code, response.reason)
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
application_introspection_content = response.json()
|
||||||
|
except ValueError:
|
||||||
|
log.exception("Introspection: Failed to parse response as json")
|
||||||
|
return None
|
||||||
|
|
||||||
|
client_owner_value = None
|
||||||
|
if "active" in application_introspection_content and application_introspection_content["active"] is True:
|
||||||
|
if "client_owner" in application_introspection_content:
|
||||||
|
client_owner_value = application_introspection_content["client_owner"]
|
||||||
|
|
||||||
|
if client_owner_value:
|
||||||
|
client_owner, _ = UserModel.objects.get_or_create(pk=client_owner_value)
|
||||||
|
else:
|
||||||
|
client_owner = None
|
||||||
|
|
||||||
|
if "username" in content:
|
||||||
|
user = self.get_or_create_user_from_content(content)
|
||||||
|
else:
|
||||||
|
user = None
|
||||||
|
|
||||||
|
max_caching_time = datetime.now() + timedelta(
|
||||||
|
seconds=oauth2_settings.RESOURCE_SERVER_TOKEN_CACHING_SECONDS
|
||||||
|
)
|
||||||
|
|
||||||
|
if "exp" in content:
|
||||||
|
expires = datetime.utcfromtimestamp(content["exp"])
|
||||||
|
if expires > max_caching_time:
|
||||||
|
expires = max_caching_time
|
||||||
|
else:
|
||||||
|
expires = max_caching_time
|
||||||
|
|
||||||
|
scope = content.get("scope", "")
|
||||||
|
|
||||||
|
if settings.USE_TZ:
|
||||||
|
expires = make_aware(
|
||||||
|
expires, timezone=get_timezone(oauth2_settings.AUTHENTICATION_SERVER_EXP_TIME_ZONE)
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO: get application owner and put it here
|
||||||
|
access_token, _created = AccessToken.objects.update_or_create(
|
||||||
|
token=token,
|
||||||
|
defaults={
|
||||||
|
"user": user,
|
||||||
|
"client_id": content.get("client_id", ""),
|
||||||
|
"client_owner": client_owner,
|
||||||
|
"application": None,
|
||||||
|
"scope": scope,
|
||||||
|
"expires": expires,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return access_token
|
||||||
0
apps/users/__init__.py
Normal file
5
apps/users/admin.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
from .models import User
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(User)
|
||||||
6
apps/users/apps.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class UsersConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.users'
|
||||||
0
apps/users/constans.py
Normal file
27
apps/users/forms.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
from django import forms
|
||||||
|
from django.contrib.auth import authenticate, get_user_model
|
||||||
|
from django.contrib.auth.forms import UsernameField
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.text import capfirst
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from .models import OAuthCode
|
||||||
|
|
||||||
|
UserModel = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class OAUTHLoginRequestForm(forms.ModelForm):
|
||||||
|
"""
|
||||||
|
Base class for authenticating users. Extend this to get a form that accepts
|
||||||
|
username/password logins.
|
||||||
|
"""
|
||||||
|
agreement = forms.BooleanField(required=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = OAuthCode
|
||||||
|
fields = ['agreement']
|
||||||
|
|
||||||
|
|
||||||
|
class AccountUpdateForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = UserModel
|
||||||
|
fields = ['iban']
|
||||||
50
apps/users/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-07-03 14:33
|
||||||
|
|
||||||
|
import apps.users.models
|
||||||
|
import django.contrib.auth.validators
|
||||||
|
import django.utils.timezone
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='User',
|
||||||
|
fields=[
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||||
|
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||||
|
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||||
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('uuid', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('username', models.CharField(blank=True, error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, null=True, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||||
|
('last_checkout_request', models.DateTimeField(blank=True, max_length=30, null=True, verbose_name='otp expire')),
|
||||||
|
('balance', models.BigIntegerField(default=0, verbose_name='balance')),
|
||||||
|
('iban', models.CharField(blank=True, max_length=30, null=True)),
|
||||||
|
('iban_verified', models.BooleanField(null=True)),
|
||||||
|
('last_update', models.DateTimeField(auto_now=True, max_length=30, null=True, verbose_name='last update')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'user',
|
||||||
|
'verbose_name_plural': 'users',
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
managers=[
|
||||||
|
('objects', apps.users.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
22
apps/users/migrations/0002_oauthcode.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-07-10 12:48
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OAuthCode',
|
||||||
|
fields=[
|
||||||
|
('uuid', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||||
|
('code_verifier', models.CharField(blank=True, max_length=64, null=True)),
|
||||||
|
('code', models.CharField(blank=True, max_length=64, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
18
apps/users/migrations/0003_alter_oauthcode_code_verifier.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-07-10 13:30
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0002_oauthcode'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='oauthcode',
|
||||||
|
name='code_verifier',
|
||||||
|
field=models.CharField(blank=True, max_length=100, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
apps/users/migrations/0004_alter_oauthcode_code_verifier.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 5.0.6 on 2024-07-10 13:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0003_alter_oauthcode_code_verifier'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='oauthcode',
|
||||||
|
name='code_verifier',
|
||||||
|
field=models.CharField(blank=True, max_length=130, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
0
apps/users/migrations/__init__.py
Normal file
112
apps/users/models.py
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
import requests
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.auth.base_user import BaseUserManager
|
||||||
|
from django.contrib.auth.models import AbstractUser
|
||||||
|
from django.contrib.auth.validators import UnicodeUsernameValidator
|
||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
import uuid
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UserManager(BaseUserManager):
|
||||||
|
use_in_migrations = True
|
||||||
|
|
||||||
|
def _create_user(self, pk, **extra_fields):
|
||||||
|
user = self.model(pk=pk **extra_fields)
|
||||||
|
user.date_joined = timezone.now()
|
||||||
|
user.save(using=self._db)
|
||||||
|
return user
|
||||||
|
|
||||||
|
def create_user(self, pk, **extra_fields):
|
||||||
|
extra_fields.setdefault('is_staff', False)
|
||||||
|
extra_fields.setdefault('is_superuser', False)
|
||||||
|
|
||||||
|
return self._create_user(pk=pk, **extra_fields)
|
||||||
|
|
||||||
|
def create_superuser(self, username, email, password, **extra_fields):
|
||||||
|
if not username:
|
||||||
|
raise ValueError('The given username must be set')
|
||||||
|
|
||||||
|
extra_fields.setdefault('is_staff', True)
|
||||||
|
extra_fields.setdefault('is_superuser', True)
|
||||||
|
|
||||||
|
if extra_fields.get('is_staff') is not True:
|
||||||
|
raise ValueError('Superuser must have is_staff=True.')
|
||||||
|
if extra_fields.get('is_superuser') is not True:
|
||||||
|
raise ValueError('Superuser must have is_superuser=True.')
|
||||||
|
|
||||||
|
return self._create_user(username=username, email=email, password=password, **extra_fields)
|
||||||
|
|
||||||
|
|
||||||
|
class User(AbstractUser):
|
||||||
|
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True)
|
||||||
|
password = models.CharField(_('password'), max_length=128)
|
||||||
|
username_validator = UnicodeUsernameValidator()
|
||||||
|
username = models.CharField(
|
||||||
|
_('username'),
|
||||||
|
max_length=150,
|
||||||
|
unique=True,
|
||||||
|
help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
|
||||||
|
validators=[username_validator],
|
||||||
|
error_messages={
|
||||||
|
'unique': _("A user with that username already exists."),
|
||||||
|
},
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
last_update = models.DateTimeField(_('last update'), max_length=30, blank=True, null=True, auto_now=True)
|
||||||
|
last_login = models.DateTimeField(_('last login'), blank=True, null=True)
|
||||||
|
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
|
||||||
|
|
||||||
|
objects = UserManager()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.username or self.pk)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: user redis instead.
|
||||||
|
# TODO: create a queryset
|
||||||
|
class OAuthCode(models.Model):
|
||||||
|
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True)
|
||||||
|
code_verifier = models.CharField(max_length=130, blank=True, null=True)
|
||||||
|
code = models.CharField(max_length=64, blank=True, null=True)
|
||||||
|
|
||||||
|
def generate_code_verifier(self):
|
||||||
|
code_verifier = ''.join(
|
||||||
|
random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(43, 128)))
|
||||||
|
self.code_verifier = code_verifier
|
||||||
|
self.save()
|
||||||
|
return code_verifier
|
||||||
|
|
||||||
|
def generate_code_challenge(self):
|
||||||
|
code_challenge = hashlib.sha256(self.code_verifier.encode('utf-8')).digest()
|
||||||
|
code_challenge = base64.urlsafe_b64encode(code_challenge).decode('utf-8').replace('=', '')
|
||||||
|
return code_challenge
|
||||||
|
|
||||||
|
def generate_login_url(self):
|
||||||
|
url = f'''{settings.BASE_OAUTH2_PROVIDER_URL}/oauth2/authorize/?response_type=code&code_challenge={self.generate_code_challenge()}&code_challenge_method=S256&client_id={settings.CLIENT_ID}&scope=wallet.wallet:get_balance+wallet.transaction:list&state={self.uuid}'''
|
||||||
|
return url
|
||||||
|
|
||||||
|
def validate_code(self):
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
"client_id": settings.CLIENT_ID,
|
||||||
|
"client_secret": settings.CLIENT_SECRET,
|
||||||
|
"code": self.code,
|
||||||
|
"code_verifier": self.code_verifier,
|
||||||
|
# "redirect_uri=http://127.0.0.1:8000/noexist/callback",
|
||||||
|
"grant_type": "authorization_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(f'{settings.BASE_OAUTH2_PROVIDER_URL}/oauth2/token/', data=data, headers=headers)
|
||||||
|
|
||||||
|
return response.json()
|
||||||
3
apps/users/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
11
apps/users/urls.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
from django.urls import path
|
||||||
|
from .views import OAUTHLoginRequestView, OAUTHLoginCallbackView, AccountDetailView, AccountUpdateView
|
||||||
|
|
||||||
|
app_name = "users"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('login/request/', OAUTHLoginRequestView.as_view(), name='login_request'),
|
||||||
|
path('login/callback/', OAUTHLoginCallbackView.as_view(), name='login_calback'),
|
||||||
|
path('account/', AccountDetailView.as_view(), name='account_detail'),
|
||||||
|
path('account/update/', AccountUpdateView.as_view(), name='account_update'),
|
||||||
|
]
|
||||||
102
apps/users/views.py
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth import login
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.generic import DetailView, CreateView, UpdateView
|
||||||
|
from rest_framework import generics
|
||||||
|
|
||||||
|
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
||||||
|
from apps.gooyal_oauth2.validators import OAuth2Validator
|
||||||
|
from apps.users.forms import OAUTHLoginRequestForm, AccountUpdateForm
|
||||||
|
from apps.users.models import User, OAuthCode
|
||||||
|
|
||||||
|
UserModel = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class OAUTHLoginRequestView(CreateView):
|
||||||
|
model = OAuthCode
|
||||||
|
form_class = OAUTHLoginRequestForm
|
||||||
|
template_name = 'users/login_request.html'
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
self.object: OAuthCode = form.save(commit=False)
|
||||||
|
self.object.generate_code_verifier()
|
||||||
|
self.object.save()
|
||||||
|
return HttpResponseRedirect(self.object.generate_login_url())
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
for error_key in form.errors:
|
||||||
|
for error_list in form.errors[error_key].data:
|
||||||
|
for error in error_list:
|
||||||
|
key = _(error_key)
|
||||||
|
messages.error(self.request, f"{key}: {error}")
|
||||||
|
|
||||||
|
return super().form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class OAUTHLoginCallbackView(DetailView):
|
||||||
|
model = OAuthCode
|
||||||
|
template_name = 'users/login_callback.html'
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
state = self.request.GET.get('state')
|
||||||
|
code = self.request.GET.get('code')
|
||||||
|
obj = OAuthCode.objects.filter(pk=state).first()
|
||||||
|
if obj:
|
||||||
|
obj.code = code
|
||||||
|
obj.save()
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def introspect_token(self, token):
|
||||||
|
url = settings.OAUTH2_PROVIDER['RESOURCE_SERVER_INTROSPECTION_URL']
|
||||||
|
credentials = settings.OAUTH2_PROVIDER['RESOURCE_SERVER_INTROSPECTION_CREDENTIALS']
|
||||||
|
validator = OAuth2Validator()
|
||||||
|
access_token = validator._get_token_from_authentication_server(token, url, None, credentials)
|
||||||
|
return access_token
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
data = self.object.validate_code()
|
||||||
|
# {'access_token': 'WhnIAfci6yIyTsh63PPqM1HXfXqKvr', 'expires_in': 36000, 'token_type': 'Bearer',
|
||||||
|
# 'scope': 'wallet.wallet:get_balance wallet.transaction:list',
|
||||||
|
# 'refresh_token': 'qRHFXXc3R3EiQKUq5BcSY2b9xuTm0d'}
|
||||||
|
#
|
||||||
|
# from oauth2_provider.models import get_access_token_model, get_application_model
|
||||||
|
access_token = self.introspect_token(data['access_token'])
|
||||||
|
login(self.request, access_token.user, backend='django.contrib.auth.backends.ModelBackend')
|
||||||
|
# AccessToken = get_access_token_model()
|
||||||
|
# access_token = AccessToken.objects.create(
|
||||||
|
# token=data['access_token'],
|
||||||
|
# scope=data['scope'],
|
||||||
|
# expires=timezone.now() + timedelta(data['scope']),
|
||||||
|
# )
|
||||||
|
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
class AccountDetailView(DetailView):
|
||||||
|
model = User
|
||||||
|
template_name = 'users/account_detail.html'
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
return self.request.user
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
class AccountUpdateView(UpdateView):
|
||||||
|
model = User
|
||||||
|
template_name = 'users/account_update.html'
|
||||||
|
form_class = AccountUpdateForm
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
messages.success(self.request, 'عملیات انجام شد.')
|
||||||
|
return reverse('users:account_detail')
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
return self.request.user
|
||||||
0
data_crud/__init__.py
Normal file
16
data_crud/asgi.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""
|
||||||
|
ASGI config for data_crud project.
|
||||||
|
|
||||||
|
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'data_crud.settings')
|
||||||
|
|
||||||
|
application = get_asgi_application()
|
||||||
124
data_crud/settings.py
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
"""
|
||||||
|
Django settings for data_crud project.
|
||||||
|
|
||||||
|
Generated by 'django-admin startproject' using Django 5.0.6.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.0/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/5.0/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = 'django-insecure-e_%y)zafwj5!_euf29(fv5ku#yw*#6nl%)ukiku2i8o#c@donr'
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = 'data_crud.urls'
|
||||||
|
|
||||||
|
TEMPLATES = [
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [BASE_DIR / 'templates']
|
||||||
|
,
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = 'data_crud.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': BASE_DIR / 'db.sqlite3',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/5.0/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
|
TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/5.0/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = 'static/'
|
||||||
|
|
||||||
|
# Default primary key field type
|
||||||
|
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||||
|
|
||||||
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
22
data_crud/urls.py
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
"""
|
||||||
|
URL configuration for data_crud project.
|
||||||
|
|
||||||
|
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||||
|
https://docs.djangoproject.com/en/5.0/topics/http/urls/
|
||||||
|
Examples:
|
||||||
|
Function views
|
||||||
|
1. Add an import: from my_app import views
|
||||||
|
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||||
|
Class-based views
|
||||||
|
1. Add an import: from other_app.views import Home
|
||||||
|
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||||
|
Including another URLconf
|
||||||
|
1. Import the include() function: from django.urls import include, path
|
||||||
|
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||||
|
"""
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('admin/', admin.site.urls),
|
||||||
|
]
|
||||||
16
data_crud/wsgi.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""
|
||||||
|
WSGI config for data_crud project.
|
||||||
|
|
||||||
|
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'data_crud.settings')
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
||||||
22
manage.py
Executable file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""Django's command-line utility for administrative tasks."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run administrative tasks."""
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'data_crud.settings')
|
||||||
|
try:
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
except ImportError as exc:
|
||||||
|
raise ImportError(
|
||||||
|
"Couldn't import Django. Are you sure it's installed and "
|
||||||
|
"available on your PYTHONPATH environment variable? Did you "
|
||||||
|
"forget to activate a virtual environment?"
|
||||||
|
) from exc
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
21
requirements.in
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# requirements.in
|
||||||
|
django
|
||||||
|
django-oauth-toolkit
|
||||||
|
djangorestframework
|
||||||
|
markdown
|
||||||
|
django-filter
|
||||||
|
django-cors-headers
|
||||||
|
pillow
|
||||||
|
python-decouple
|
||||||
|
gunicorn
|
||||||
|
gevent
|
||||||
|
redis
|
||||||
|
celery
|
||||||
|
drf-spectacular
|
||||||
|
django-crispy-forms
|
||||||
|
crispy_bootstrap5
|
||||||
|
django-jalali-date
|
||||||
|
django-betterforms
|
||||||
|
django-crispy-forms
|
||||||
|
crispy_bootstrap5
|
||||||
|
psycopg[binary]
|
||||||
166
requirements.txt
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
#
|
||||||
|
# This file is autogenerated by pip-compile with Python 3.11
|
||||||
|
# by the following command:
|
||||||
|
#
|
||||||
|
# pip-compile
|
||||||
|
#
|
||||||
|
amqp==5.2.0
|
||||||
|
# via kombu
|
||||||
|
asgiref==3.8.1
|
||||||
|
# via
|
||||||
|
# django
|
||||||
|
# django-cors-headers
|
||||||
|
async-timeout==4.0.3
|
||||||
|
# via redis
|
||||||
|
attrs==23.2.0
|
||||||
|
# via
|
||||||
|
# jsonschema
|
||||||
|
# referencing
|
||||||
|
billiard==4.2.0
|
||||||
|
# via celery
|
||||||
|
celery==5.4.0
|
||||||
|
# via -r requirements.in
|
||||||
|
certifi==2024.6.2
|
||||||
|
# via requests
|
||||||
|
cffi==1.16.0
|
||||||
|
# via cryptography
|
||||||
|
charset-normalizer==3.3.2
|
||||||
|
# via requests
|
||||||
|
click==8.1.7
|
||||||
|
# via
|
||||||
|
# celery
|
||||||
|
# click-didyoumean
|
||||||
|
# click-plugins
|
||||||
|
# click-repl
|
||||||
|
click-didyoumean==0.3.1
|
||||||
|
# via celery
|
||||||
|
click-plugins==1.1.1
|
||||||
|
# via celery
|
||||||
|
click-repl==0.3.0
|
||||||
|
# via celery
|
||||||
|
crispy-bootstrap5==2024.2
|
||||||
|
# via -r requirements.in
|
||||||
|
cryptography==42.0.8
|
||||||
|
# via jwcrypto
|
||||||
|
django==5.0.6
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# crispy-bootstrap5
|
||||||
|
# django-betterforms
|
||||||
|
# django-cors-headers
|
||||||
|
# django-crispy-forms
|
||||||
|
# django-filter
|
||||||
|
# django-jalali-date
|
||||||
|
# django-oauth-toolkit
|
||||||
|
# djangorestframework
|
||||||
|
# drf-spectacular
|
||||||
|
django-betterforms==2.0.0
|
||||||
|
# via -r requirements.in
|
||||||
|
django-cors-headers==4.4.0
|
||||||
|
# via -r requirements.in
|
||||||
|
django-crispy-forms==2.2
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# crispy-bootstrap5
|
||||||
|
django-filter==24.2
|
||||||
|
# via -r requirements.in
|
||||||
|
django-jalali-date==1.1.3
|
||||||
|
# via -r requirements.in
|
||||||
|
django-oauth-toolkit==2.4.0
|
||||||
|
# via -r requirements.in
|
||||||
|
django-proxy==1.3.0
|
||||||
|
# via -r requirements.in
|
||||||
|
djangorestframework==3.15.2
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# drf-spectacular
|
||||||
|
drf-spectacular==0.27.2
|
||||||
|
# via -r requirements.in
|
||||||
|
gevent==24.2.1
|
||||||
|
# via -r requirements.in
|
||||||
|
greenlet==3.0.3
|
||||||
|
# via gevent
|
||||||
|
gunicorn==22.0.0
|
||||||
|
# via -r requirements.in
|
||||||
|
idna==3.7
|
||||||
|
# via requests
|
||||||
|
inflection==0.5.1
|
||||||
|
# via drf-spectacular
|
||||||
|
jalali-core==1.0.0
|
||||||
|
# via jdatetime
|
||||||
|
jdatetime==5.0.0
|
||||||
|
# via django-jalali-date
|
||||||
|
jsonschema==4.22.0
|
||||||
|
# via drf-spectacular
|
||||||
|
jsonschema-specifications==2023.12.1
|
||||||
|
# via jsonschema
|
||||||
|
jwcrypto==1.5.6
|
||||||
|
# via django-oauth-toolkit
|
||||||
|
kombu==5.3.7
|
||||||
|
# via celery
|
||||||
|
markdown==3.6
|
||||||
|
# via -r requirements.in
|
||||||
|
oauthlib==3.2.2
|
||||||
|
# via django-oauth-toolkit
|
||||||
|
packaging==24.1
|
||||||
|
# via gunicorn
|
||||||
|
pillow==10.3.0
|
||||||
|
# via -r requirements.in
|
||||||
|
prompt-toolkit==3.0.47
|
||||||
|
# via click-repl
|
||||||
|
psycopg[binary]==3.1.19
|
||||||
|
# via -r requirements.in
|
||||||
|
psycopg-binary==3.1.19
|
||||||
|
# via psycopg
|
||||||
|
pycparser==2.22
|
||||||
|
# via cffi
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
# via celery
|
||||||
|
python-decouple==3.8
|
||||||
|
# via -r requirements.in
|
||||||
|
pytz==2024.1
|
||||||
|
# via django-oauth-toolkit
|
||||||
|
pyyaml==6.0.1
|
||||||
|
# via drf-spectacular
|
||||||
|
redis==5.0.6
|
||||||
|
# via -r requirements.in
|
||||||
|
referencing==0.35.1
|
||||||
|
# via
|
||||||
|
# jsonschema
|
||||||
|
# jsonschema-specifications
|
||||||
|
requests==2.32.3
|
||||||
|
# via
|
||||||
|
# django-oauth-toolkit
|
||||||
|
# django-proxy
|
||||||
|
rpds-py==0.18.1
|
||||||
|
# via
|
||||||
|
# jsonschema
|
||||||
|
# referencing
|
||||||
|
six==1.16.0
|
||||||
|
# via python-dateutil
|
||||||
|
sqlparse==0.5.0
|
||||||
|
# via django
|
||||||
|
typing-extensions==4.12.2
|
||||||
|
# via
|
||||||
|
# jwcrypto
|
||||||
|
# psycopg
|
||||||
|
tzdata==2024.1
|
||||||
|
# via celery
|
||||||
|
uritemplate==4.1.1
|
||||||
|
# via drf-spectacular
|
||||||
|
urllib3==2.2.2
|
||||||
|
# via requests
|
||||||
|
vine==5.1.0
|
||||||
|
# via
|
||||||
|
# amqp
|
||||||
|
# celery
|
||||||
|
# kombu
|
||||||
|
wcwidth==0.2.13
|
||||||
|
# via prompt-toolkit
|
||||||
|
zope-event==5.0
|
||||||
|
# via gevent
|
||||||
|
zope-interface==6.4.post2
|
||||||
|
# via gevent
|
||||||
|
|
||||||
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
|
# setuptools
|
||||||
8
run.sh
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
while ! nc -z $DB_HOST 5432 ; do
|
||||||
|
echo "APP Waiting for the DB Server"
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
|
#python3 manage.py collectstatic --noinput
|
||||||
|
python3 manage.py migrate
|
||||||
|
gunicorn data_crud.wsgi:application --bind 0.0.0.0:8000 -w 4
|
||||||
275
static/admin/css/autocomplete.css
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
select.admin-autocomplete {
|
||||||
|
width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container {
|
||||||
|
min-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--single,
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--multiple {
|
||||||
|
min-height: 30px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--focus .select2-selection,
|
||||||
|
.select2-container--admin-autocomplete.select2-container--open .select2-selection {
|
||||||
|
border-color: var(--body-quiet-color);
|
||||||
|
min-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--single,
|
||||||
|
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--single {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--multiple,
|
||||||
|
.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--multiple {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--single {
|
||||||
|
background-color: var(--body-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: var(--body-fg);
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder {
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow {
|
||||||
|
height: 26px;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 1px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: #888 transparent transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px 4px 0 4px;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
||||||
|
left: 1px;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single {
|
||||||
|
background-color: var(--darkened-bg);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single .select2-selection__clear {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #888 transparent;
|
||||||
|
border-width: 0 4px 5px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--multiple {
|
||||||
|
background-color: var(--body-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
box-sizing: border-box;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 10px 5px 5px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder {
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
margin-top: 5px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 5px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice {
|
||||||
|
background-color: var(--darkened-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: default;
|
||||||
|
float: left;
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||||
|
color: var(--body-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-search--inline {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple {
|
||||||
|
border: solid var(--body-quiet-color) 1px;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple {
|
||||||
|
background-color: var(--darkened-bg);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--disabled .select2-selection__choice__remove {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--multiple {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--multiple {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-search--dropdown {
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field {
|
||||||
|
background: var(--body-bg);
|
||||||
|
color: var(--body-fg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-search--inline .select2-search__field {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--body-fg);
|
||||||
|
border: none;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results > .select2-results__options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
color: var(--body-fg);
|
||||||
|
background: var(--body-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option[role=group] {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] {
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option[aria-selected=true] {
|
||||||
|
background-color: var(--selected-bg);
|
||||||
|
color: var(--body-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option .select2-results__option {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__group {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -1em;
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -2em;
|
||||||
|
padding-left: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -3em;
|
||||||
|
padding-left: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -4em;
|
||||||
|
padding-left: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -5em;
|
||||||
|
padding-left: 6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] {
|
||||||
|
background-color: var(--primary);
|
||||||
|
color: var(--primary-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--admin-autocomplete .select2-results__group {
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
1156
static/admin/css/base.css
Normal file
338
static/admin/css/changelists.css
Normal file
|
|
@ -0,0 +1,338 @@
|
||||||
|
/* CHANGELISTS */
|
||||||
|
|
||||||
|
#changelist {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .changelist-form-container {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-list .hiddenfields { display:none; }
|
||||||
|
|
||||||
|
.change-list .filtered table {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-list .filtered {
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-list .filtered .results, .change-list .filtered .paginator,
|
||||||
|
.filtered #toolbar, .filtered div.xfull {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-list .filtered table tbody th {
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-form .results {
|
||||||
|
overflow-x: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .toplinks {
|
||||||
|
border-bottom: 1px solid var(--hairline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .paginator {
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
border-bottom: 1px solid var(--hairline-color);
|
||||||
|
background: var(--body-bg);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CHANGELIST TABLES */
|
||||||
|
|
||||||
|
#changelist table thead th {
|
||||||
|
padding: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist table thead th.action-checkbox-column {
|
||||||
|
width: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist table tbody td.action-checkbox {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist table tfoot {
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TOOLBAR */
|
||||||
|
|
||||||
|
#toolbar {
|
||||||
|
padding: 8px 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-top: 1px solid var(--hairline-color);
|
||||||
|
border-bottom: 1px solid var(--hairline-color);
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toolbar form input {
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
padding: 5px;
|
||||||
|
color: var(--body-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toolbar #searchbar {
|
||||||
|
height: 1.1875rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
padding: 2px 5px;
|
||||||
|
margin: 0;
|
||||||
|
vertical-align: top;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toolbar #searchbar:focus {
|
||||||
|
border-color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toolbar form input[type="submit"] {
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
padding: 4px 8px;
|
||||||
|
margin: 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
background: var(--body-bg);
|
||||||
|
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--body-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#toolbar form input[type="submit"]:focus,
|
||||||
|
#toolbar form input[type="submit"]:hover {
|
||||||
|
border-color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-search img {
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-search .help {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FILTER COLUMN */
|
||||||
|
|
||||||
|
#changelist-filter {
|
||||||
|
flex: 0 0 240px;
|
||||||
|
order: 1;
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
border-left: none;
|
||||||
|
margin: 0 0 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter h2 {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
padding: 5px 15px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter h3,
|
||||||
|
#changelist-filter details summary {
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 0 15px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter details summary > * {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter details > summary {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter details > summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter details > summary::before {
|
||||||
|
content: '→';
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter details[open] > summary::before {
|
||||||
|
content: '↓';
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter ul {
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 0 15px 15px;
|
||||||
|
border-bottom: 1px solid var(--hairline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter ul:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter li {
|
||||||
|
list-style-type: none;
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter a {
|
||||||
|
display: block;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter li.selected {
|
||||||
|
border-left: 5px solid var(--hairline-color);
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-left: -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter li.selected a {
|
||||||
|
color: var(--link-selected-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter a:focus, #changelist-filter a:hover,
|
||||||
|
#changelist-filter li.selected a:focus,
|
||||||
|
#changelist-filter li.selected a:hover {
|
||||||
|
color: var(--link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter #changelist-filter-extra-actions {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-bottom: 1px solid var(--hairline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DATE DRILLDOWN */
|
||||||
|
|
||||||
|
.change-list .toplinks {
|
||||||
|
display: flex;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 3px 17px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-list .toplinks a {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-list .toplinks .date-back {
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-list .toplinks .date-back:focus,
|
||||||
|
.change-list .toplinks .date-back:hover {
|
||||||
|
color: var(--link-hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ACTIONS */
|
||||||
|
|
||||||
|
.filtered .actions {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist table input {
|
||||||
|
margin: 0;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Once the :has() pseudo-class is supported by all browsers, the tr.selected
|
||||||
|
selector and the JS adding the class can be removed. */
|
||||||
|
#changelist tbody tr.selected {
|
||||||
|
background-color: var(--selected-row);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist tbody tr:has(.action-select:checked) {
|
||||||
|
background-color: var(--selected-row);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (forced-colors: active) {
|
||||||
|
#changelist tbody tr.selected {
|
||||||
|
background-color: SelectedItem;
|
||||||
|
}
|
||||||
|
#changelist tbody tr:has(.action-select:checked) {
|
||||||
|
background-color: SelectedItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions {
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--body-bg);
|
||||||
|
border-top: none;
|
||||||
|
border-bottom: none;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions span.all,
|
||||||
|
#changelist .actions span.action-counter,
|
||||||
|
#changelist .actions span.clear,
|
||||||
|
#changelist .actions span.question {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
margin: 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions select {
|
||||||
|
vertical-align: top;
|
||||||
|
height: 1.5rem;
|
||||||
|
color: var(--body-fg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
padding: 0 0 0 4px;
|
||||||
|
margin: 0;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions select:focus {
|
||||||
|
border-color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions label {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions .button {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--body-bg);
|
||||||
|
box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 4px 8px;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--body-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions .button:focus, #changelist .actions .button:hover {
|
||||||
|
border-color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
124
static/admin/css/dark_mode.css
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--primary: #264b5d;
|
||||||
|
--primary-fg: #f7f7f7;
|
||||||
|
|
||||||
|
--body-fg: #eeeeee;
|
||||||
|
--body-bg: #121212;
|
||||||
|
--body-quiet-color: #e0e0e0;
|
||||||
|
--body-loud-color: #ffffff;
|
||||||
|
|
||||||
|
--breadcrumbs-link-fg: #e0e0e0;
|
||||||
|
--breadcrumbs-bg: var(--primary);
|
||||||
|
|
||||||
|
--link-fg: #81d4fa;
|
||||||
|
--link-hover-color: #4ac1f7;
|
||||||
|
--link-selected-fg: #6f94c6;
|
||||||
|
|
||||||
|
--hairline-color: #272727;
|
||||||
|
--border-color: #353535;
|
||||||
|
|
||||||
|
--error-fg: #e35f5f;
|
||||||
|
--message-success-bg: #006b1b;
|
||||||
|
--message-warning-bg: #583305;
|
||||||
|
--message-error-bg: #570808;
|
||||||
|
|
||||||
|
--darkened-bg: #212121;
|
||||||
|
--selected-bg: #1b1b1b;
|
||||||
|
--selected-row: #00363a;
|
||||||
|
|
||||||
|
--close-button-bg: #333333;
|
||||||
|
--close-button-hover-bg: #666666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
html[data-theme="dark"] {
|
||||||
|
--primary: #264b5d;
|
||||||
|
--primary-fg: #f7f7f7;
|
||||||
|
|
||||||
|
--body-fg: #eeeeee;
|
||||||
|
--body-bg: #121212;
|
||||||
|
--body-quiet-color: #e0e0e0;
|
||||||
|
--body-loud-color: #ffffff;
|
||||||
|
|
||||||
|
--breadcrumbs-link-fg: #e0e0e0;
|
||||||
|
--breadcrumbs-bg: var(--primary);
|
||||||
|
|
||||||
|
--link-fg: #81d4fa;
|
||||||
|
--link-hover-color: #4ac1f7;
|
||||||
|
--link-selected-fg: #6f94c6;
|
||||||
|
|
||||||
|
--hairline-color: #272727;
|
||||||
|
--border-color: #353535;
|
||||||
|
|
||||||
|
--error-fg: #e35f5f;
|
||||||
|
--message-success-bg: #006b1b;
|
||||||
|
--message-warning-bg: #583305;
|
||||||
|
--message-error-bg: #570808;
|
||||||
|
|
||||||
|
--darkened-bg: #212121;
|
||||||
|
--selected-bg: #1b1b1b;
|
||||||
|
--selected-row: #00363a;
|
||||||
|
|
||||||
|
--close-button-bg: #333333;
|
||||||
|
--close-button-hover-bg: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* THEME SWITCH */
|
||||||
|
.theme-toggle {
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-inline-start: 5px;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle svg {
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fully hide screen reader text so we only show the one matching the current
|
||||||
|
theme.
|
||||||
|
*/
|
||||||
|
.theme-toggle .visually-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="auto"] .theme-toggle .theme-label-when-auto {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="dark"] .theme-toggle .theme-label-when-dark {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .theme-toggle .theme-label-when-light {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ICONS */
|
||||||
|
.theme-toggle svg.theme-icon-when-auto,
|
||||||
|
.theme-toggle svg.theme-icon-when-dark,
|
||||||
|
.theme-toggle svg.theme-icon-when-light {
|
||||||
|
fill: var(--header-link-color);
|
||||||
|
color: var(--header-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="auto"] .theme-toggle svg.theme-icon-when-auto {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="dark"] .theme-toggle svg.theme-icon-when-dark {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .theme-toggle svg.theme-icon-when-light {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
29
static/admin/css/dashboard.css
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* DASHBOARD */
|
||||||
|
.dashboard td, .dashboard th {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard .module table th {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard .module table td {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard .module table td a {
|
||||||
|
display: block;
|
||||||
|
padding-right: .6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RECENT ACTIONS MODULE */
|
||||||
|
|
||||||
|
.module ul.actionlist {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.actionlist li {
|
||||||
|
list-style-type: none;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
20
static/admin/css/fonts.css
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
src: url('../fonts/Roboto-Bold-webfont.woff');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
src: url('../fonts/Roboto-Regular-webfont.woff');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Roboto';
|
||||||
|
src: url('../fonts/Roboto-Light-webfont.woff');
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
534
static/admin/css/forms.css
Normal file
|
|
@ -0,0 +1,534 @@
|
||||||
|
@import url('widgets.css');
|
||||||
|
|
||||||
|
/* FORM ROWS */
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
border-bottom: 1px solid var(--hairline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row img, .form-row input {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row label input[type="checkbox"] {
|
||||||
|
margin-top: 0;
|
||||||
|
vertical-align: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .form-row p {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-multiline {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-multiline > div {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FORM LABELS */
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: normal;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.required label, label.required {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--body-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RADIO BUTTONS */
|
||||||
|
|
||||||
|
form div.radiolist div {
|
||||||
|
padding-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.radiolist.inline div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.radiolist label {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.radiolist input[type="radio"] {
|
||||||
|
margin: -2px 4px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form ul.inline {
|
||||||
|
margin-left: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form ul.inline li {
|
||||||
|
float: left;
|
||||||
|
padding-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ALIGNED FIELDSETS */
|
||||||
|
|
||||||
|
.aligned label {
|
||||||
|
display: block;
|
||||||
|
padding: 4px 10px 0 0;
|
||||||
|
min-width: 160px;
|
||||||
|
width: 160px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned label:not(.vCheckboxLabel):after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 1.625rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned label + p, .aligned .checkbox-row + div.help, .aligned label + div.readonly {
|
||||||
|
padding: 6px 0;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned ul label {
|
||||||
|
display: inline;
|
||||||
|
float: none;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .form-row input {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {
|
||||||
|
width: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned ul {
|
||||||
|
margin-left: 160px;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned div.radiolist {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned p.help,
|
||||||
|
form .aligned div.help {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 160px;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned p.date div.help.timezonewarning,
|
||||||
|
form .aligned p.datetime div.help.timezonewarning,
|
||||||
|
form .aligned p.time div.help.timezonewarning {
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned p.help:last-child,
|
||||||
|
form .aligned div.help:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned input + p.help,
|
||||||
|
form .aligned textarea + p.help,
|
||||||
|
form .aligned select + p.help,
|
||||||
|
form .aligned input + div.help,
|
||||||
|
form .aligned textarea + div.help,
|
||||||
|
form .aligned select + div.help {
|
||||||
|
margin-left: 160px;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned ul li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned table p {
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .vCheckboxLabel {
|
||||||
|
float: none;
|
||||||
|
width: auto;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: -3px;
|
||||||
|
padding: 0 0 5px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .vCheckboxLabel + p.help,
|
||||||
|
.aligned .vCheckboxLabel + div.help {
|
||||||
|
margin-top: -4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {
|
||||||
|
width: 610px;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset .fieldBox {
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WIDE FIELDSETS */
|
||||||
|
|
||||||
|
.wide label {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .wide p,
|
||||||
|
form .wide ul.errorlist,
|
||||||
|
form .wide input + p.help,
|
||||||
|
form .wide input + div.help {
|
||||||
|
margin-left: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .wide p.help,
|
||||||
|
form .wide div.help {
|
||||||
|
padding-left: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.help ul {
|
||||||
|
padding-left: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* COLLAPSED FIELDSETS */
|
||||||
|
|
||||||
|
fieldset.collapsed * {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset.collapsed h2, fieldset.collapsed {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset.collapsed {
|
||||||
|
border: 1px solid var(--hairline-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset.collapsed h2 {
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset .collapse-toggle {
|
||||||
|
color: var(--header-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset.collapsed .collapse-toggle {
|
||||||
|
background: transparent;
|
||||||
|
display: inline;
|
||||||
|
color: var(--link-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MONOSPACE TEXTAREAS */
|
||||||
|
|
||||||
|
fieldset.monospace textarea {
|
||||||
|
font-family: var(--font-family-monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SUBMIT ROW */
|
||||||
|
|
||||||
|
.submit-row {
|
||||||
|
padding: 12px 14px 12px;
|
||||||
|
margin: 0 0 20px;
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
border: 1px solid var(--hairline-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.popup .submit-row {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row input {
|
||||||
|
height: 2.1875rem;
|
||||||
|
line-height: 0.9375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row input, .submit-row a {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row input.default {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row a.deletelink {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row a.deletelink {
|
||||||
|
display: block;
|
||||||
|
background: var(--delete-button-bg);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.625rem 0.9375rem;
|
||||||
|
height: 0.9375rem;
|
||||||
|
line-height: 0.9375rem;
|
||||||
|
color: var(--button-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row a.closelink {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--close-button-bg);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 10px 15px;
|
||||||
|
height: 0.9375rem;
|
||||||
|
line-height: 0.9375rem;
|
||||||
|
color: var(--button-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row a.deletelink:focus,
|
||||||
|
.submit-row a.deletelink:hover,
|
||||||
|
.submit-row a.deletelink:active {
|
||||||
|
background: var(--delete-button-hover-bg);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row a.closelink:focus,
|
||||||
|
.submit-row a.closelink:hover,
|
||||||
|
.submit-row a.closelink:active {
|
||||||
|
background: var(--close-button-hover-bg);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CUSTOM FORM FIELDS */
|
||||||
|
|
||||||
|
.vSelectMultipleField {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vCheckboxField {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vDateField, .vTimeField {
|
||||||
|
margin-right: 2px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vDateField {
|
||||||
|
min-width: 6.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vTimeField {
|
||||||
|
min-width: 4.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vURLField {
|
||||||
|
width: 30em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vLargeTextField, .vXMLLargeTextField {
|
||||||
|
width: 48em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flatpages-flatpage #id_content {
|
||||||
|
height: 40.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module table .vPositiveSmallIntegerField {
|
||||||
|
width: 2.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vIntegerField {
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vBigIntegerField {
|
||||||
|
width: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vForeignKeyRawIdAdminField {
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vTextField, .vUUIDField {
|
||||||
|
width: 20em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* INLINES */
|
||||||
|
|
||||||
|
.inline-group {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group thead th {
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group .aligned label {
|
||||||
|
width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-related {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-related h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
border-top: 1px solid var(--hairline-color);
|
||||||
|
border-bottom: 1px solid var(--hairline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-related h3 span.delete {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-related h3 span.delete label {
|
||||||
|
margin-left: 2px;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-related fieldset {
|
||||||
|
margin: 0;
|
||||||
|
background: var(--body-bg);
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-related fieldset.module h3 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 2px 5px 3px 5px;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
background: #bcd;
|
||||||
|
color: var(--body-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group .tabular fieldset.module {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-related.tabular fieldset.module table {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.last-related fieldset {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group .tabular tr.has_original td {
|
||||||
|
padding-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group .tabular tr td.original {
|
||||||
|
padding: 2px 0 0 0;
|
||||||
|
width: 0;
|
||||||
|
_position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group .tabular th.original {
|
||||||
|
width: 0px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group .tabular td.original p {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
height: 1.1em;
|
||||||
|
padding: 2px 9px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 0.5625rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
_width: 700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group ul.tools {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group ul.tools li {
|
||||||
|
display: inline;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group div.add-row,
|
||||||
|
.inline-group .tabular tr.add-row td {
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-bottom: 1px solid var(--hairline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group .tabular tr.add-row td {
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-bottom: 1px solid var(--hairline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group ul.tools a.add,
|
||||||
|
.inline-group div.add-row a,
|
||||||
|
.inline-group .tabular tr.add-row td a {
|
||||||
|
background: url(../img/icon-addlink.svg) 0 1px no-repeat;
|
||||||
|
padding-left: 16px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-form {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RELATED FIELD ADD ONE / LOOKUP */
|
||||||
|
|
||||||
|
.related-lookup {
|
||||||
|
margin-left: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.related-lookup {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
background-image: url(../img/search.svg);
|
||||||
|
}
|
||||||
|
|
||||||
|
form .related-widget-wrapper ul {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearable-file-input input {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
61
static/admin/css/login.css
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
/* LOGIN FORM */
|
||||||
|
|
||||||
|
.login {
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login #header {
|
||||||
|
height: auto;
|
||||||
|
padding: 15px 16px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login #header h1 {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login #header h1 a {
|
||||||
|
color: var(--header-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login #content {
|
||||||
|
padding: 20px 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login #container {
|
||||||
|
background: var(--body-bg);
|
||||||
|
border: 1px solid var(--hairline-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 28em;
|
||||||
|
min-width: 300px;
|
||||||
|
margin: 100px auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .form-row {
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .form-row label {
|
||||||
|
display: block;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .form-row #id_username, .login .form-row #id_password {
|
||||||
|
padding: 8px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .submit-row {
|
||||||
|
padding: 1em 0 0 0;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .password-reset-link {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
150
static/admin/css/nav_sidebar.css
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
.sticky {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-nav-sidebar {
|
||||||
|
z-index: 20;
|
||||||
|
left: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 0 0 23px;
|
||||||
|
width: 23px;
|
||||||
|
border: 0;
|
||||||
|
border-right: 1px solid var(--hairline-color);
|
||||||
|
background-color: var(--body-bg);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: var(--link-fg);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .toggle-nav-sidebar {
|
||||||
|
border-left: 1px solid var(--hairline-color);
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-nav-sidebar:hover,
|
||||||
|
.toggle-nav-sidebar:focus {
|
||||||
|
background-color: var(--darkened-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-sidebar {
|
||||||
|
z-index: 15;
|
||||||
|
flex: 0 0 275px;
|
||||||
|
left: -276px;
|
||||||
|
margin-left: -276px;
|
||||||
|
border-top: 1px solid transparent;
|
||||||
|
border-right: 1px solid var(--hairline-color);
|
||||||
|
background-color: var(--body-bg);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] #nav-sidebar {
|
||||||
|
border-left: 1px solid var(--hairline-color);
|
||||||
|
border-right: 0;
|
||||||
|
left: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
right: -276px;
|
||||||
|
margin-right: -276px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-nav-sidebar::before {
|
||||||
|
content: '\00BB';
|
||||||
|
}
|
||||||
|
|
||||||
|
.main.shifted .toggle-nav-sidebar::before {
|
||||||
|
content: '\00AB';
|
||||||
|
}
|
||||||
|
|
||||||
|
.main > #nav-sidebar {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main.shifted > #nav-sidebar {
|
||||||
|
margin-left: 0;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .main.shifted > #nav-sidebar {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-sidebar .module th {
|
||||||
|
width: 100%;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-sidebar .module th,
|
||||||
|
#nav-sidebar .module caption {
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-sidebar .module td {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] #nav-sidebar .module th,
|
||||||
|
[dir="rtl"] #nav-sidebar .module caption {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-sidebar .current-app .section:link,
|
||||||
|
#nav-sidebar .current-app .section:visited {
|
||||||
|
color: var(--header-color);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-sidebar .current-model {
|
||||||
|
background: var(--selected-row);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (forced-colors: active) {
|
||||||
|
#nav-sidebar .current-model {
|
||||||
|
background-color: SelectedItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main > #nav-sidebar + .content {
|
||||||
|
max-width: calc(100% - 23px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main.shifted > #nav-sidebar + .content {
|
||||||
|
max-width: calc(100% - 299px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#nav-sidebar, #toggle-nav-sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main > #nav-sidebar + .content,
|
||||||
|
.main.shifted > #nav-sidebar + .content {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-filter {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 2px 5px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
background-color: var(--darkened-bg);
|
||||||
|
color: var(--body-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-filter:focus {
|
||||||
|
border-color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-filter.no-results {
|
||||||
|
background: var(--message-error-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-sidebar table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
970
static/admin/css/responsive.css
Normal file
|
|
@ -0,0 +1,970 @@
|
||||||
|
/* Tablets */
|
||||||
|
|
||||||
|
input[type="submit"], button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
/* Basic */
|
||||||
|
|
||||||
|
html {
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Layout */
|
||||||
|
|
||||||
|
#container {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
padding: 15px 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.breadcrumbs {
|
||||||
|
padding: 10px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
|
||||||
|
#header {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 15px 30px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#site-name {
|
||||||
|
margin: 0 0 8px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-tools {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.85;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-tools a {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dashboard */
|
||||||
|
|
||||||
|
.dashboard #content {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-related {
|
||||||
|
margin-right: -290px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colSM #content-related {
|
||||||
|
margin-left: -290px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colMS {
|
||||||
|
margin-right: 290px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colSM {
|
||||||
|
margin-left: 290px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard .module table td a {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td .changelink, td .addlink {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Changelist */
|
||||||
|
|
||||||
|
#toolbar {
|
||||||
|
border: none;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-search > div {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
max-width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-search label {
|
||||||
|
line-height: 1.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toolbar form #searchbar {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
width: 0;
|
||||||
|
height: 1.375rem;
|
||||||
|
margin: 0 10px 0 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toolbar form input[type=submit] {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-search .quiet {
|
||||||
|
width: 0;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
margin: 5px 0 0 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions label {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions select {
|
||||||
|
background: var(--body-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions .button {
|
||||||
|
min-width: 48px;
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions span.all,
|
||||||
|
#changelist .actions span.clear,
|
||||||
|
#changelist .actions span.question,
|
||||||
|
#changelist .actions span.action-counter {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
margin: 0 10px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter {
|
||||||
|
flex-basis: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-list .filtered .results,
|
||||||
|
.change-list .filtered .paginator,
|
||||||
|
.filtered #toolbar,
|
||||||
|
.filtered .actions,
|
||||||
|
|
||||||
|
#changelist .paginator {
|
||||||
|
border-top-color: var(--hairline-color); /* XXX Is this used at all? */
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .results + .paginator {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms */
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row input[type=text],
|
||||||
|
.form-row input[type=password],
|
||||||
|
.form-row input[type=email],
|
||||||
|
.form-row input[type=url],
|
||||||
|
.form-row input[type=tel],
|
||||||
|
.form-row input[type=number],
|
||||||
|
.form-row textarea,
|
||||||
|
.form-row select,
|
||||||
|
.form-row .vTextField {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 6px 8px;
|
||||||
|
min-height: 2.25rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row select {
|
||||||
|
height: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row select[multiple] {
|
||||||
|
height: auto;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset .fieldBox + .fieldBox {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px solid var(--hairline-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned label {
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .related-lookup,
|
||||||
|
.aligned .datetimeshortcuts,
|
||||||
|
.aligned .related-lookup + strong {
|
||||||
|
align-self: center;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned div.radiolist {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row a.deletelink {
|
||||||
|
padding: 10px 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button, input[type=submit], input[type=button], .submit-row input, a.button {
|
||||||
|
padding: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selector */
|
||||||
|
|
||||||
|
.selector {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-filter {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-filter label {
|
||||||
|
margin: 0 8px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-filter input {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-available, .selector-chosen {
|
||||||
|
width: auto;
|
||||||
|
flex: 1 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector select {
|
||||||
|
width: 100%;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector ul.selector-chooser {
|
||||||
|
width: 26px;
|
||||||
|
height: 52px;
|
||||||
|
padding: 2px 0;
|
||||||
|
border-radius: 20px;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-add, .selector-remove {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-size: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-add {
|
||||||
|
background-position: 0 -120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-remove {
|
||||||
|
background-position: 0 -80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.selector-chooseall, a.selector-clearall {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked {
|
||||||
|
flex-direction: column;
|
||||||
|
max-width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked > * {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked select {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-available, .stacked .selector-chosen {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked ul.selector-chooser {
|
||||||
|
width: 52px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 2px;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-chooser li {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-add, .stacked .selector-remove {
|
||||||
|
background-size: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-add {
|
||||||
|
background-position: 0 -40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .active.selector-add {
|
||||||
|
background-position: 0 -40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-add:focus, .active.selector-add:hover {
|
||||||
|
background-position: 0 -140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .active.selector-add:focus, .stacked .active.selector-add:hover {
|
||||||
|
background-position: 0 -60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-remove {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .active.selector-remove {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-remove:focus, .active.selector-remove:hover {
|
||||||
|
background-position: 0 -100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .active.selector-remove:focus, .stacked .active.selector-remove:hover {
|
||||||
|
background-position: 0 -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-tooltip, .selector .help-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetime input {
|
||||||
|
width: 50%;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetime span {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetime .timezonewarning {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetimeshortcuts {
|
||||||
|
color: var(--border-color); /* XXX Redundant, .datetime span also sets #ccc */
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
|
||||||
|
width: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Messages */
|
||||||
|
|
||||||
|
ul.messagelist li {
|
||||||
|
padding-left: 55px;
|
||||||
|
background-position: 30px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.messagelist li.error {
|
||||||
|
background-position: 30px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.messagelist li.warning {
|
||||||
|
background-position: 30px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login */
|
||||||
|
|
||||||
|
.login #header {
|
||||||
|
padding: 15px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login #site-name {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GIS */
|
||||||
|
|
||||||
|
div.olMap {
|
||||||
|
max-width: calc(100vw - 30px);
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.olMap + .clear_features {
|
||||||
|
display: block;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Docs */
|
||||||
|
|
||||||
|
.module table.xfull {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.literal-block {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile */
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
/* Layout */
|
||||||
|
|
||||||
|
#header, #content, #footer {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer:empty {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.breadcrumbs {
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dashboard */
|
||||||
|
|
||||||
|
.colMS, .colSM {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-related, .colSM #content-related {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-related .module {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-related .module h2 {
|
||||||
|
padding: 10px 15px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Changelist */
|
||||||
|
|
||||||
|
#changelist {
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toolbar {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions label {
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions select {
|
||||||
|
flex: 1 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions span {
|
||||||
|
flex: 1 0 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter {
|
||||||
|
position: static;
|
||||||
|
width: auto;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.object-tools {
|
||||||
|
float: none;
|
||||||
|
margin: 0 0 15px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.object-tools li {
|
||||||
|
height: auto;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.object-tools li + li {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms */
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .form-row,
|
||||||
|
.aligned .form-row > div {
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .form-row > div {
|
||||||
|
width: calc(100vw - 30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-container {
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-container.checkbox-row {
|
||||||
|
flex-flow: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vURLField {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset .fieldBox + .fieldBox {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset.collapsed .form-row {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned label {
|
||||||
|
width: 100%;
|
||||||
|
min-width: auto;
|
||||||
|
padding: 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned label:after {
|
||||||
|
max-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .form-row input,
|
||||||
|
.aligned .form-row select,
|
||||||
|
.aligned .form-row textarea {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .checkbox-row input {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .vCheckboxLabel {
|
||||||
|
flex: 1 0;
|
||||||
|
padding: 1px 0 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned label + p,
|
||||||
|
.aligned label + div.help,
|
||||||
|
.aligned label + div.readonly {
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned p.file-upload {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.clearable-file-input {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.clearable-file-input label {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .timezonewarning {
|
||||||
|
flex: 1 0 100%;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned .form-row div.help {
|
||||||
|
width: 100%;
|
||||||
|
margin: 5px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned ul,
|
||||||
|
form .aligned ul.errorlist {
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned div.radiolist {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-right: 15px;
|
||||||
|
margin-bottom: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned div.radiolist:not(.inline) div + div {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Related widget */
|
||||||
|
|
||||||
|
.related-widget-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.related-widget-wrapper .selector {
|
||||||
|
order: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.related-widget-wrapper > a {
|
||||||
|
order: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.related-widget-wrapper .radiolist ~ a {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.related-widget-wrapper > select ~ a {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selector */
|
||||||
|
|
||||||
|
.selector {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-available, .selector-chosen {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector select {
|
||||||
|
max-height: 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector ul.selector-chooser {
|
||||||
|
display: block;
|
||||||
|
width: 52px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 2px;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector ul.selector-chooser li {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-remove {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-remove:focus, .active.selector-remove:hover {
|
||||||
|
background-position: 0 -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-add {
|
||||||
|
background-position: 0 -40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-add:focus, .active.selector-add:hover {
|
||||||
|
background-position: 0 -60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inlines */
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .inline-related {
|
||||||
|
border: 1px solid var(--hairline-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 15px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .inline-related > * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .inline-related .module {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .inline-related .module .form-row {
|
||||||
|
border-top: 1px solid var(--hairline-color);
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .inline-related .module .form-row:first-child {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .inline-related h3 {
|
||||||
|
padding: 10px;
|
||||||
|
border-top-width: 0;
|
||||||
|
border-bottom-width: 2px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .inline-related h3 .inline_label {
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .inline-related h3 span.delete {
|
||||||
|
float: none;
|
||||||
|
flex: 1 1 100%;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .aligned .form-row > div:not([class]) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] .aligned label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group[data-inline-type="stacked"] div.add-row {
|
||||||
|
margin-top: 15px;
|
||||||
|
border: 1px solid var(--hairline-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group div.add-row,
|
||||||
|
.inline-group .tabular tr.add-row td {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-group div.add-row a,
|
||||||
|
.inline-group .tabular tr.add-row td a {
|
||||||
|
display: block;
|
||||||
|
padding: 8px 10px 8px 26px;
|
||||||
|
background-position: 8px 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Submit row */
|
||||||
|
|
||||||
|
.submit-row {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0 0 15px;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row input, .submit-row input.default, .submit-row a {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row a.closelink {
|
||||||
|
padding: 10px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row a.deletelink {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Messages */
|
||||||
|
|
||||||
|
ul.messagelist li {
|
||||||
|
padding-left: 40px;
|
||||||
|
background-position: 15px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.messagelist li.error {
|
||||||
|
background-position: 15px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.messagelist li.warning {
|
||||||
|
background-position: 15px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Paginator */
|
||||||
|
|
||||||
|
.paginator .this-page, .paginator a:link, .paginator a:visited {
|
||||||
|
padding: 4px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Login */
|
||||||
|
|
||||||
|
body.login {
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login #container {
|
||||||
|
width: auto;
|
||||||
|
max-width: 480px;
|
||||||
|
margin: 50px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login #header,
|
||||||
|
.login #content {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login #content-main {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .form-row {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .form-row + .form-row {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .form-row label {
|
||||||
|
margin: 0 0 5px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .submit-row {
|
||||||
|
padding: 15px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login br {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login .submit-row input {
|
||||||
|
margin: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errornote {
|
||||||
|
margin: 0 0 20px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calendar and clock */
|
||||||
|
|
||||||
|
.calendarbox, .clockbox {
|
||||||
|
position: fixed !important;
|
||||||
|
top: 50% !important;
|
||||||
|
left: 50% !important;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox:before, .clockbox:before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0, 0, 0, 0.75);
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox > *, .clockbox > * {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox > div:first-child {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox .calendar, .clockbox h2 {
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox .calendar-cancel, .clockbox .calendar-cancel {
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-shortcuts {
|
||||||
|
padding: 10px 0;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-shortcuts a {
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timelist a {
|
||||||
|
background: var(--body-bg);
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-cancel {
|
||||||
|
padding: 8px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clockbox h2 {
|
||||||
|
padding: 8px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar caption {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
|
||||||
|
z-index: 1;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* History */
|
||||||
|
|
||||||
|
table#change-history tbody th, table#change-history tbody td {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
table#change-history tbody th {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Docs */
|
||||||
|
|
||||||
|
table.model tbody th, table.model tbody td {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
84
static/admin/css/responsive_rtl.css
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
/* TABLETS */
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
[dir="rtl"] .colMS {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] #user-tools {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] #changelist .actions label {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] #changelist .actions select {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .change-list .filtered .results,
|
||||||
|
[dir="rtl"] .change-list .filtered .paginator,
|
||||||
|
[dir="rtl"] .filtered #toolbar,
|
||||||
|
[dir="rtl"] .filtered div.xfull,
|
||||||
|
[dir="rtl"] .filtered .actions,
|
||||||
|
[dir="rtl"] #changelist-filter {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .inline-group ul.tools a.add,
|
||||||
|
[dir="rtl"] .inline-group div.add-row a,
|
||||||
|
[dir="rtl"] .inline-group .tabular tr.add-row td a {
|
||||||
|
padding: 8px 26px 8px 10px;
|
||||||
|
background-position: calc(100% - 8px) 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .related-widget-wrapper-link + .selector {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .selector .selector-filter label {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .object-tools li {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .object-tools li + li {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .dashboard .module table td a {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MOBILE */
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
[dir="rtl"] .aligned .related-lookup,
|
||||||
|
[dir="rtl"] .aligned .datetimeshortcuts {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .aligned ul,
|
||||||
|
[dir="rtl"] form .aligned ul.errorlist {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] #changelist-filter {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
[dir="rtl"] .aligned .vCheckboxLabel {
|
||||||
|
padding: 1px 5px 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
302
static/admin/css/rtl.css
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
/* GLOBAL */
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module h2, .module caption {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module ul, .module ol {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewlink, .addlink, .changelink, .hidelink {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 16px;
|
||||||
|
background-position: 100% 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deletelink {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 16px;
|
||||||
|
background-position: 100% 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.object-tools {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th:first-child,
|
||||||
|
tfoot td:first-child {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LAYOUT */
|
||||||
|
|
||||||
|
#user-tools {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.breadcrumbs {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-main {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-related {
|
||||||
|
float: left;
|
||||||
|
margin-left: -300px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colMS {
|
||||||
|
margin-left: 300px;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SORTABLE TABLES */
|
||||||
|
|
||||||
|
table thead th.sorted .sortoptions {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead th.sorted .text {
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dashboard styles */
|
||||||
|
|
||||||
|
.dashboard .module table td a {
|
||||||
|
padding-left: .6em;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* changelists styles */
|
||||||
|
|
||||||
|
.change-list .filtered table {
|
||||||
|
border-left: none;
|
||||||
|
border-right: 0px none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter {
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist-filter li.selected {
|
||||||
|
border-left: none;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-left: 0;
|
||||||
|
border-right: 5px solid var(--hairline-color);
|
||||||
|
padding-right: 10px;
|
||||||
|
margin-right: -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist table tbody td:first-child, #changelist table tbody th:first-child {
|
||||||
|
border-right: none;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paginator .end {
|
||||||
|
margin-left: 6px;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paginator input {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FORMS */
|
||||||
|
|
||||||
|
.aligned label {
|
||||||
|
padding: 0 0 3px 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row a.deletelink {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vDateField, .vTimeField {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aligned .form-row input {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned ul {
|
||||||
|
margin-right: 163px;
|
||||||
|
padding-right: 10px;
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form ul.inline li {
|
||||||
|
float: right;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .aligned p.help,
|
||||||
|
form .aligned div.help {
|
||||||
|
margin-right: 160px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.help ul,
|
||||||
|
form .aligned .checkbox-row + .help,
|
||||||
|
form .aligned p.date div.help.timezonewarning,
|
||||||
|
form .aligned p.datetime div.help.timezonewarning,
|
||||||
|
form .aligned p.time div.help.timezonewarning {
|
||||||
|
margin-right: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .wide p.help, form .wide div.help {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .wide p,
|
||||||
|
form .wide ul.errorlist,
|
||||||
|
form .wide input + p.help,
|
||||||
|
form .wide input + div.help {
|
||||||
|
margin-right: 200px;
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-row {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset .fieldBox {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorlist li {
|
||||||
|
background-position: 100% 12px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errornote {
|
||||||
|
background-position: 100% 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WIDGETS */
|
||||||
|
|
||||||
|
.calendarnav-previous {
|
||||||
|
top: 0;
|
||||||
|
left: auto;
|
||||||
|
right: 10px;
|
||||||
|
background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox .calendarnav-previous:focus,
|
||||||
|
.calendarbox .calendarnav-previous:hover {
|
||||||
|
background-position: 0 -45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarnav-next {
|
||||||
|
top: 0;
|
||||||
|
right: auto;
|
||||||
|
left: 10px;
|
||||||
|
background: url(../img/calendar-icons.svg) 0 0 no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox .calendarnav-next:focus,
|
||||||
|
.calendarbox .calendarnav-next:hover {
|
||||||
|
background-position: 0 -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar caption, .calendarbox h2 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-filter {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-add {
|
||||||
|
background: url(../img/selector-icons.svg) 0 -64px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-add:focus, .active.selector-add:hover {
|
||||||
|
background-position: 0 -80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-remove {
|
||||||
|
background: url(../img/selector-icons.svg) 0 -96px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-remove:focus, .active.selector-remove:hover {
|
||||||
|
background-position: 0 -112px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.selector-chooseall {
|
||||||
|
background: url(../img/selector-icons.svg) right -128px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active.selector-chooseall:focus, a.active.selector-chooseall:hover {
|
||||||
|
background-position: 100% -144px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.selector-clearall {
|
||||||
|
background: url(../img/selector-icons.svg) 0 -160px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active.selector-clearall:focus, a.active.selector-clearall:hover {
|
||||||
|
background-position: 0 -176px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-deletelink {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
form .form-row p.datetime {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.related-widget-wrapper {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MISC */
|
||||||
|
|
||||||
|
.inline-related h2, .inline-group h2 {
|
||||||
|
text-align: right
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-related h3 span.delete {
|
||||||
|
padding-right: 20px;
|
||||||
|
padding-left: inherit;
|
||||||
|
left: 10px;
|
||||||
|
right: inherit;
|
||||||
|
float:left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-related h3 span.delete label {
|
||||||
|
margin-left: inherit;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-chooser {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
21
static/admin/css/vendor/select2/LICENSE-SELECT2.md
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2012-2017 Kevin Brown, Igor Vaynberg, and Select2 contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
481
static/admin/css/vendor/select2/select2.css
vendored
Normal file
|
|
@ -0,0 +1,481 @@
|
||||||
|
.select2-container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle; }
|
||||||
|
.select2-container .select2-selection--single {
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
height: 28px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none; }
|
||||||
|
.select2-container .select2-selection--single .select2-selection__rendered {
|
||||||
|
display: block;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap; }
|
||||||
|
.select2-container .select2-selection--single .select2-selection__clear {
|
||||||
|
position: relative; }
|
||||||
|
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-left: 20px; }
|
||||||
|
.select2-container .select2-selection--multiple {
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
min-height: 32px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none; }
|
||||||
|
.select2-container .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: 8px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap; }
|
||||||
|
.select2-container .select2-search--inline {
|
||||||
|
float: left; }
|
||||||
|
.select2-container .select2-search--inline .select2-search__field {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: none;
|
||||||
|
font-size: 100%;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0; }
|
||||||
|
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none; }
|
||||||
|
|
||||||
|
.select2-dropdown {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
left: -100000px;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1051; }
|
||||||
|
|
||||||
|
.select2-results {
|
||||||
|
display: block; }
|
||||||
|
|
||||||
|
.select2-results__options {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
.select2-results__option {
|
||||||
|
padding: 6px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none; }
|
||||||
|
.select2-results__option[aria-selected] {
|
||||||
|
cursor: pointer; }
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown {
|
||||||
|
left: 0; }
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown--above {
|
||||||
|
border-bottom: none;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0; }
|
||||||
|
|
||||||
|
.select2-container--open .select2-dropdown--below {
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0; }
|
||||||
|
|
||||||
|
.select2-search--dropdown {
|
||||||
|
display: block;
|
||||||
|
padding: 4px; }
|
||||||
|
.select2-search--dropdown .select2-search__field {
|
||||||
|
padding: 4px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box; }
|
||||||
|
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
|
||||||
|
-webkit-appearance: none; }
|
||||||
|
.select2-search--dropdown.select2-search--hide {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.select2-close-mask {
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: block;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 99;
|
||||||
|
background-color: #fff;
|
||||||
|
filter: alpha(opacity=0); }
|
||||||
|
|
||||||
|
.select2-hidden-accessible {
|
||||||
|
border: 0 !important;
|
||||||
|
clip: rect(0 0 0 0) !important;
|
||||||
|
-webkit-clip-path: inset(50%) !important;
|
||||||
|
clip-path: inset(50%) !important;
|
||||||
|
height: 1px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
position: absolute !important;
|
||||||
|
width: 1px !important;
|
||||||
|
white-space: nowrap !important; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--single {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: #444;
|
||||||
|
line-height: 28px; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__placeholder {
|
||||||
|
color: #999; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||||
|
height: 26px;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 1px;
|
||||||
|
width: 20px; }
|
||||||
|
.select2-container--default .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: #888 transparent transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px 4px 0 4px;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 0; }
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
||||||
|
float: left; }
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
||||||
|
left: 1px;
|
||||||
|
right: auto; }
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection--single {
|
||||||
|
background-color: #eee;
|
||||||
|
cursor: default; }
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #888 transparent;
|
||||||
|
border-width: 0 4px 5px 4px; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-selection--multiple {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: text; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
box-sizing: border-box;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 5px;
|
||||||
|
width: 100%; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
|
||||||
|
list-style: none; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-right: 10px;
|
||||||
|
padding: 1px; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__choice {
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: default;
|
||||||
|
float: left;
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0 5px; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 2px; }
|
||||||
|
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||||
|
color: #333; }
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
|
||||||
|
float: right; }
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: auto; }
|
||||||
|
|
||||||
|
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: auto; }
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--focus .select2-selection--multiple {
|
||||||
|
border: solid black 1px;
|
||||||
|
outline: 0; }
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection--multiple {
|
||||||
|
background-color: #eee;
|
||||||
|
cursor: default; }
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0; }
|
||||||
|
|
||||||
|
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-search--dropdown .select2-search__field {
|
||||||
|
border: 1px solid #aaa; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-search--inline .select2-search__field {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
-webkit-appearance: textfield; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-results > .select2-results__options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option[role=group] {
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option[aria-disabled=true] {
|
||||||
|
color: #999; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option[aria-selected=true] {
|
||||||
|
background-color: #ddd; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option {
|
||||||
|
padding-left: 1em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
|
||||||
|
padding-left: 0; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -1em;
|
||||||
|
padding-left: 2em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -2em;
|
||||||
|
padding-left: 3em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -3em;
|
||||||
|
padding-left: 4em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -4em;
|
||||||
|
padding-left: 5em; }
|
||||||
|
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
||||||
|
margin-left: -5em;
|
||||||
|
padding-left: 6em; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
||||||
|
background-color: #5897fb;
|
||||||
|
color: white; }
|
||||||
|
|
||||||
|
.select2-container--default .select2-results__group {
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
padding: 6px; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--single {
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: 0;
|
||||||
|
background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
|
||||||
|
background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
|
||||||
|
background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
|
||||||
|
.select2-container--classic .select2-selection--single:focus {
|
||||||
|
border: 1px solid #5897fb; }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__rendered {
|
||||||
|
color: #444;
|
||||||
|
line-height: 28px; }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__clear {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 10px; }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
|
||||||
|
color: #999; }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__arrow {
|
||||||
|
background-color: #ddd;
|
||||||
|
border: none;
|
||||||
|
border-left: 1px solid #aaa;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
height: 26px;
|
||||||
|
position: absolute;
|
||||||
|
top: 1px;
|
||||||
|
right: 1px;
|
||||||
|
width: 20px;
|
||||||
|
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
|
||||||
|
background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
|
||||||
|
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: #888 transparent transparent transparent;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 5px 4px 0 4px;
|
||||||
|
height: 0;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-top: -2px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 0; }
|
||||||
|
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
||||||
|
float: left; }
|
||||||
|
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
||||||
|
border: none;
|
||||||
|
border-right: 1px solid #aaa;
|
||||||
|
border-radius: 0;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
left: 1px;
|
||||||
|
right: auto; }
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--single {
|
||||||
|
border: 1px solid #5897fb; }
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
|
||||||
|
background: transparent;
|
||||||
|
border: none; }
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
||||||
|
border-color: transparent transparent #888 transparent;
|
||||||
|
border-width: 0 4px 5px 4px; }
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
|
||||||
|
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
|
||||||
|
background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
|
||||||
|
border-bottom: none;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
|
||||||
|
background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
|
||||||
|
background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-selection--multiple {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: text;
|
||||||
|
outline: 0; }
|
||||||
|
.select2-container--classic .select2-selection--multiple:focus {
|
||||||
|
border: 1px solid #5897fb; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 5px; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
|
||||||
|
display: none; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
|
||||||
|
background-color: #e4e4e4;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: default;
|
||||||
|
float: left;
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 0 5px; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
color: #888;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 2px; }
|
||||||
|
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
|
||||||
|
color: #555; }
|
||||||
|
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: auto; }
|
||||||
|
|
||||||
|
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-right: auto; }
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open .select2-selection--multiple {
|
||||||
|
border: 1px solid #5897fb; }
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
|
||||||
|
border-top: none;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0; }
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
|
||||||
|
border-bottom: none;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-search--dropdown .select2-search__field {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
outline: 0; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-search--inline .select2-search__field {
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-dropdown {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid transparent; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-dropdown--above {
|
||||||
|
border-bottom: none; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-dropdown--below {
|
||||||
|
border-top: none; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results > .select2-results__options {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results__option[role=group] {
|
||||||
|
padding: 0; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results__option[aria-disabled=true] {
|
||||||
|
color: grey; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
|
||||||
|
background-color: #3875d7;
|
||||||
|
color: white; }
|
||||||
|
|
||||||
|
.select2-container--classic .select2-results__group {
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
padding: 6px; }
|
||||||
|
|
||||||
|
.select2-container--classic.select2-container--open .select2-dropdown {
|
||||||
|
border-color: #5897fb; }
|
||||||
1
static/admin/css/vendor/select2/select2.min.css
vendored
Normal file
603
static/admin/css/widgets.css
Normal file
|
|
@ -0,0 +1,603 @@
|
||||||
|
/* SELECTOR (FILTER INTERFACE) */
|
||||||
|
|
||||||
|
.selector {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
gap: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector select {
|
||||||
|
height: 17.2em;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
overflow: scroll;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-available, .selector-chosen {
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-available h2, .selector-chosen h2 {
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-chosen .list-footer-display {
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-top: none;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
margin: 0 0 10px;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: center;
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--header-link-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.selector-chosen .list-footer-display__clear {
|
||||||
|
color: var(--breadcrumbs-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-chosen h2 {
|
||||||
|
background: var(--secondary);
|
||||||
|
color: var(--header-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-available h2 {
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-filter {
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-width: 0 1px;
|
||||||
|
padding: 8px;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
font-size: 0.625rem;
|
||||||
|
margin: 0;
|
||||||
|
text-align: left;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-filter label,
|
||||||
|
.inline-group .aligned .selector .selector-filter label {
|
||||||
|
float: left;
|
||||||
|
margin: 7px 0 0;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1;
|
||||||
|
min-width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-filter input {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-available input,
|
||||||
|
.selector .selector-chosen input {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector ul.selector-chooser {
|
||||||
|
align-self: center;
|
||||||
|
width: 22px;
|
||||||
|
background-color: var(--selected-bg);
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
transform: translateY(-17px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-chooser li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 3px;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector select {
|
||||||
|
padding: 0 10px;
|
||||||
|
margin: 0 0 10px;
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
}
|
||||||
|
.selector .selector-chosen--with-filtered select {
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
height: 14em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-chosen:not(.selector-chosen--with-filtered) .list-footer-display {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-add, .selector-remove {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
display: block;
|
||||||
|
text-indent: -3000px;
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-add, .active.selector-remove {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-add:hover, .active.selector-remove:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-add {
|
||||||
|
background: url(../img/selector-icons.svg) 0 -96px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-add:focus, .active.selector-add:hover {
|
||||||
|
background-position: 0 -112px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector-remove {
|
||||||
|
background: url(../img/selector-icons.svg) 0 -64px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active.selector-remove:focus, .active.selector-remove:hover {
|
||||||
|
background-position: 0 -80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.selector-chooseall, a.selector-clearall {
|
||||||
|
display: inline-block;
|
||||||
|
height: 16px;
|
||||||
|
text-align: left;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 16px;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
text-decoration: none;
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active.selector-chooseall:focus, a.active.selector-clearall:focus,
|
||||||
|
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
|
||||||
|
color: var(--link-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active.selector-chooseall, a.active.selector-clearall {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.selector-chooseall {
|
||||||
|
padding: 0 18px 0 0;
|
||||||
|
background: url(../img/selector-icons.svg) right -160px no-repeat;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active.selector-chooseall:focus, a.active.selector-chooseall:hover {
|
||||||
|
background-position: 100% -176px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.selector-clearall {
|
||||||
|
padding: 0 0 0 18px;
|
||||||
|
background: url(../img/selector-icons.svg) 0 -128px no-repeat;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.active.selector-clearall:focus, a.active.selector-clearall:hover {
|
||||||
|
background-position: 0 -144px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* STACKED SELECTORS */
|
||||||
|
|
||||||
|
.stacked {
|
||||||
|
float: left;
|
||||||
|
width: 490px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked select {
|
||||||
|
width: 480px;
|
||||||
|
height: 10.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-available, .stacked .selector-chosen {
|
||||||
|
width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-available {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-available input {
|
||||||
|
width: 422px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked ul.selector-chooser {
|
||||||
|
height: 22px;
|
||||||
|
width: 50px;
|
||||||
|
margin: 0 0 10px 40%;
|
||||||
|
background-color: #eee;
|
||||||
|
border-radius: 10px;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-chooser li {
|
||||||
|
float: left;
|
||||||
|
padding: 3px 3px 3px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-chooseall, .stacked .selector-clearall {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-add {
|
||||||
|
background: url(../img/selector-icons.svg) 0 -32px no-repeat;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .active.selector-add {
|
||||||
|
background-position: 0 -32px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .active.selector-add:focus, .stacked .active.selector-add:hover {
|
||||||
|
background-position: 0 -48px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .selector-remove {
|
||||||
|
background: url(../img/selector-icons.svg) 0 0 no-repeat;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .active.selector-remove {
|
||||||
|
background-position: 0 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stacked .active.selector-remove:focus, .stacked .active.selector-remove:hover {
|
||||||
|
background-position: 0 -16px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .help-icon {
|
||||||
|
background: url(../img/icon-unknown.svg) 0 0 no-repeat;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin: -2px 0 0 2px;
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .selector-chosen .help-icon {
|
||||||
|
background: url(../img/icon-unknown-alt.svg) 0 0 no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selector .search-label-icon {
|
||||||
|
background: url(../img/search.svg) 0 0 no-repeat;
|
||||||
|
display: inline-block;
|
||||||
|
height: 1.125rem;
|
||||||
|
width: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DATE AND TIME */
|
||||||
|
|
||||||
|
p.datetime {
|
||||||
|
line-height: 20px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetime span {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table p.datetime {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetimeshortcuts .clock-icon, .datetimeshortcuts .date-icon {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetimeshortcuts .clock-icon {
|
||||||
|
background: url(../img/icon-clock.svg) 0 0 no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetimeshortcuts a:focus .clock-icon,
|
||||||
|
.datetimeshortcuts a:hover .clock-icon {
|
||||||
|
background-position: 0 -16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetimeshortcuts .date-icon {
|
||||||
|
background: url(../img/icon-calendar.svg) 0 0 no-repeat;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.datetimeshortcuts a:focus .date-icon,
|
||||||
|
.datetimeshortcuts a:hover .date-icon {
|
||||||
|
background-position: 0 -16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timezonewarning {
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* URL */
|
||||||
|
|
||||||
|
p.url {
|
||||||
|
line-height: 20px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url a {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FILE UPLOADS */
|
||||||
|
|
||||||
|
p.file-upload {
|
||||||
|
line-height: 20px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload a {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-upload .deletelink {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.clearable-file-input label {
|
||||||
|
color: var(--body-fg);
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
display: inline;
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CALENDARS & CLOCKS */
|
||||||
|
|
||||||
|
.calendarbox, .clockbox {
|
||||||
|
margin: 5px auto;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
width: 19em;
|
||||||
|
text-align: center;
|
||||||
|
background: var(--body-bg);
|
||||||
|
color: var(--body-fg);
|
||||||
|
border: 1px solid var(--hairline-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clockbox {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar table {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
background: white;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar caption, .calendarbox h2 {
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
border-top: none;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #333;
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar th {
|
||||||
|
padding: 8px 5px;
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar td {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0;
|
||||||
|
border-top: 1px solid var(--hairline-color);
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar td.selected a {
|
||||||
|
background: var(--secondary);
|
||||||
|
color: var(--button-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar td.nonday {
|
||||||
|
background: var(--darkened-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar td.today a {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar td a, .timelist a {
|
||||||
|
display: block;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 6px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar td a:focus, .timelist a:focus,
|
||||||
|
.calendar td a:hover, .timelist a:hover {
|
||||||
|
background: var(--primary);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar td a:active, .timelist a:active {
|
||||||
|
background: var(--header-bg);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarnav {
|
||||||
|
font-size: 0.625rem;
|
||||||
|
text-align: center;
|
||||||
|
color: #ccc;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarnav a:link, #calendarnav a:visited,
|
||||||
|
#calendarnav a:focus, #calendarnav a:hover {
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-shortcuts {
|
||||||
|
background: var(--body-bg);
|
||||||
|
color: var(--body-quiet-color);
|
||||||
|
font-size: 0.6875rem;
|
||||||
|
line-height: 0.6875rem;
|
||||||
|
border-top: 1px solid var(--hairline-color);
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
text-indent: -9999px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarnav-previous {
|
||||||
|
left: 10px;
|
||||||
|
background: url(../img/calendar-icons.svg) 0 0 no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox .calendarnav-previous:focus,
|
||||||
|
.calendarbox .calendarnav-previous:hover {
|
||||||
|
background-position: 0 -15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarnav-next {
|
||||||
|
right: 10px;
|
||||||
|
background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendarbox .calendarnav-next:focus,
|
||||||
|
.calendarbox .calendarnav-next:hover {
|
||||||
|
background-position: 0 -45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-cancel {
|
||||||
|
margin: 0;
|
||||||
|
padding: 4px 0;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
background: var(--close-button-bg);
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
color: var(--button-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-cancel:focus, .calendar-cancel:hover {
|
||||||
|
background: var(--close-button-hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-cancel a {
|
||||||
|
color: var(--button-fg);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.timelist, .timelist li {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timelist a {
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EDIT INLINE */
|
||||||
|
|
||||||
|
.inline-deletelink {
|
||||||
|
float: right;
|
||||||
|
text-indent: -9999px;
|
||||||
|
background: url(../img/inline-delete.svg) 0 0 no-repeat;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 0px none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-deletelink:focus, .inline-deletelink:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RELATED WIDGET WRAPPER */
|
||||||
|
.related-widget-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: 0 10px;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.related-widget-wrapper-link {
|
||||||
|
opacity: .6;
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.related-widget-wrapper-link:link {
|
||||||
|
opacity: 1;
|
||||||
|
filter: grayscale(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GIS MAPS */
|
||||||
|
.dj_map {
|
||||||
|
width: 600px;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
202
static/admin/fonts/LICENSE.txt
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
3
static/admin/fonts/README.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
Roboto webfont source: https://www.google.com/fonts/specimen/Roboto
|
||||||
|
WOFF files extracted using https://github.com/majodev/google-webfonts-helper
|
||||||
|
Weights used in this project: Light (300), Regular (400), Bold (700)
|
||||||
BIN
static/admin/fonts/Roboto-Bold-webfont.woff
Normal file
BIN
static/admin/fonts/Roboto-Light-webfont.woff
Normal file
BIN
static/admin/fonts/Roboto-Regular-webfont.woff
Normal file
20
static/admin/img/LICENSE
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Code Charm Ltd
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
7
static/admin/img/README.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
All icons are taken from Font Awesome (http://fontawesome.io/) project.
|
||||||
|
The Font Awesome font is licensed under the SIL OFL 1.1:
|
||||||
|
- https://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
SVG icons source: https://github.com/encharm/Font-Awesome-SVG-PNG
|
||||||
|
Font-Awesome-SVG-PNG is licensed under the MIT license (see file license
|
||||||
|
in current folder).
|
||||||
14
static/admin/img/calendar-icons.svg
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<svg width="15" height="60" viewBox="0 0 1792 7168" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs>
|
||||||
|
<g id="previous">
|
||||||
|
<path d="M1037 1395l102-102q19-19 19-45t-19-45l-307-307 307-307q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-454 454q-19 19-19 45t19 45l454 454q19 19 45 19t45-19zm627-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
|
||||||
|
</g>
|
||||||
|
<g id="next">
|
||||||
|
<path d="M845 1395l454-454q19-19 19-45t-19-45l-454-454q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l307 307-307 307q-19 19-19 45t19 45l102 102q19 19 45 19t45-19zm819-499q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#previous" x="0" y="0" fill="#333333" />
|
||||||
|
<use xlink:href="#previous" x="0" y="1792" fill="#000000" />
|
||||||
|
<use xlink:href="#next" x="0" y="3584" fill="#333333" />
|
||||||
|
<use xlink:href="#next" x="0" y="5376" fill="#000000" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
static/admin/img/gis/move_vertex_off.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg width="24" height="22" viewBox="0 0 847 779" xmlns="http://www.w3.org/2000/svg"><g><path fill="#EBECE6" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120z"/><path fill="#9E9E93" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120zm607 25h-607c-26 0-50 11-67 28-17 18-28 41-28 67v536c0 27 11 50 28 68 17 17 41 27 67 27h607c26 0 49-10 67-27 17-18 28-41 28-68v-536c0-26-11-49-28-67-18-17-41-28-67-28z"/><path stroke="#A9A8A4" stroke-width="20" d="M706 295l-68 281"/><path stroke="#E47474" stroke-width="20" d="M316 648l390-353M141 435l175 213"/><path stroke="#C9C9C9" stroke-width="20" d="M319 151l-178 284M706 295l-387-144"/><g fill="#040405"><path d="M319 111c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40zM141 395c22 0 40 18 40 40s-18 40-40 40c-23 0-41-18-41-40s18-40 41-40zM316 608c22 0 40 18 40 40 0 23-18 41-40 41s-40-18-40-41c0-22 18-40 40-40zM706 254c22 0 40 18 40 41 0 22-18 40-40 40s-40-18-40-40c0-23 18-41 40-41zM638 536c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
static/admin/img/gis/move_vertex_on.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg width="24" height="22" viewBox="0 0 847 779" xmlns="http://www.w3.org/2000/svg"><g><path fill="#F1C02A" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120z"/><path fill="#9E9E93" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120zm607 25h-607c-26 0-50 11-67 28-17 18-28 41-28 67v536c0 27 11 50 28 68 17 17 41 27 67 27h607c26 0 49-10 67-27 17-18 28-41 28-68v-536c0-26-11-49-28-67-18-17-41-28-67-28z"/><path stroke="#A9A8A4" stroke-width="20" d="M706 295l-68 281"/><path stroke="#E47474" stroke-width="20" d="M316 648l390-353M141 435l175 213"/><path stroke="#C9A741" stroke-width="20" d="M319 151l-178 284M706 295l-387-144"/><g fill="#040405"><path d="M319 111c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40zM141 395c22 0 40 18 40 40s-18 40-40 40c-23 0-41-18-41-40s18-40 41-40zM316 608c22 0 40 18 40 40 0 23-18 41-40 41s-40-18-40-41c0-22 18-40 40-40zM706 254c22 0 40 18 40 41 0 22-18 40-40 40s-40-18-40-40c0-23 18-41 40-41zM638 536c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
3
static/admin/img/icon-addlink.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#70bf2b" d="M1600 796v192q0 40-28 68t-68 28h-416v416q0 40-28 68t-68 28h-192q-40 0-68-28t-28-68v-416h-416q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h416v-416q0-40 28-68t68-28h192q40 0 68 28t28 68v416h416q40 0 68 28t28 68z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 331 B |
3
static/admin/img/icon-alert.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="14" height="14" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#efb80b" d="M1024 1375v-190q0-14-9.5-23.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 23.5v190q0 14 9.5 23.5t22.5 9.5h192q13 0 22.5-9.5t9.5-23.5zm-2-374l18-459q0-12-10-19-13-11-24-11h-220q-11 0-24 11-10 7-10 21l17 457q0 10 10 16.5t24 6.5h185q14 0 23.5-6.5t10.5-16.5zm-14-934l768 1408q35 63-2 126-17 29-46.5 46t-63.5 17h-1536q-34 0-63.5-17t-46.5-46q-37-63-2-126l768-1408q17-31 47-49t65-18 65 18 47 49z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 504 B |
9
static/admin/img/icon-calendar.svg
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<svg width="16" height="32" viewBox="0 0 1792 3584" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs>
|
||||||
|
<g id="icon">
|
||||||
|
<path d="M192 1664h288v-288h-288v288zm352 0h320v-288h-320v288zm-352-352h288v-320h-288v320zm352 0h320v-320h-320v320zm-352-384h288v-288h-288v288zm736 736h320v-288h-320v288zm-384-736h320v-288h-320v288zm768 736h288v-288h-288v288zm-384-352h320v-320h-320v320zm-352-864v-288q0-13-9.5-22.5t-22.5-9.5h-64q-13 0-22.5 9.5t-9.5 22.5v288q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5-9.5t9.5-22.5zm736 864h288v-320h-288v320zm-384-384h320v-288h-320v288zm384 0h288v-288h-288v288zm32-480v-288q0-13-9.5-22.5t-22.5-9.5h-64q-13 0-22.5 9.5t-9.5 22.5v288q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5-9.5t9.5-22.5zm384-64v1280q0 52-38 90t-90 38h-1408q-52 0-90-38t-38-90v-1280q0-52 38-90t90-38h128v-96q0-66 47-113t113-47h64q66 0 113 47t47 113v96h384v-96q0-66 47-113t113-47h64q66 0 113 47t47 113v96h128q52 0 90 38t38 90z"/>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#icon" x="0" y="0" fill="#447e9b" />
|
||||||
|
<use xlink:href="#icon" x="0" y="1792" fill="#003366" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
3
static/admin/img/icon-changelink.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#efb80b" d="M491 1536l91-91-235-235-91 91v107h128v128h107zm523-928q0-22-22-22-10 0-17 7l-542 542q-7 7-7 17 0 22 22 22 10 0 17-7l542-542q7-7 7-17zm-54-192l416 416-832 832h-416v-416zm683 96q0 53-37 90l-166 166-416-416 166-165q36-38 90-38 53 0 91 38l235 234q37 39 37 91z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 380 B |
9
static/admin/img/icon-clock.svg
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<svg width="16" height="32" viewBox="0 0 1792 3584" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<defs>
|
||||||
|
<g id="icon">
|
||||||
|
<path d="M1024 544v448q0 14-9 23t-23 9h-320q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h224v-352q0-14 9-23t23-9h64q14 0 23 9t9 23zm416 352q0-148-73-273t-198-198-273-73-273 73-198 198-73 273 73 273 198 198 273 73 273-73 198-198 73-273zm224 0q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#icon" x="0" y="0" fill="#447e9b" />
|
||||||
|
<use xlink:href="#icon" x="0" y="1792" fill="#003366" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 677 B |
3
static/admin/img/icon-deletelink.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="14" height="14" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#dd4646" d="M1490 1322q0 40-28 68l-136 136q-28 28-68 28t-68-28l-294-294-294 294q-28 28-68 28t-68-28l-136-136q-28-28-28-68t28-68l294-294-294-294q-28-28-28-68t28-68l136-136q28-28 68-28t68 28l294 294 294-294q28-28 68-28t68 28l136 136q28 28 28 68t-28 68l-294 294 294 294q28 28 28 68z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 392 B |
3
static/admin/img/icon-hidelink.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#2b70bf" d="m555 1335 78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5T592 832q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-105 188-315 566t-316 567l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173T20 1029Q0 998 0 960t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5T1291 358q16 10 16 27zm37 447q0 139-79 253.5T1056 1250l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267T896 1536l74-132q212-18 392.5-137T1664 960q-115-179-282-294l63-112q95 64 182.5 153T1772 891q20 34 20 69z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 784 B |
3
static/admin/img/icon-no.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#dd4646" d="M1277 1122q0-26-19-45l-181-181 181-181q19-19 19-45 0-27-19-46l-90-90q-19-19-46-19-26 0-45 19l-181 181-181-181q-19-19-45-19-27 0-46 19l-90 90q-19 19-19 46 0 26 19 45l181 181-181 181q-19 19-19 45 0 27 19 46l90 90q19 19 46 19 26 0 45-19l181-181 181 181q19 19 45 19 27 0 46-19l90-90q19-19 19-46zm387-226q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 560 B |
3
static/admin/img/icon-unknown-alt.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#ffffff" d="M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 655 B |
3
static/admin/img/icon-unknown.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#666666" d="M1024 1376v-192q0-14-9-23t-23-9h-192q-14 0-23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23-9t9-23zm256-672q0-88-55.5-163t-138.5-116-170-41q-243 0-371 213-15 24 8 42l132 100q7 6 19 6 16 0 25-12 53-68 86-92 34-24 86-24 48 0 85.5 26t37.5 59q0 38-20 61t-68 45q-63 28-115.5 86.5t-52.5 125.5v36q0 14 9 23t23 9h192q14 0 23-9t9-23q0-19 21.5-49.5t54.5-49.5q32-18 49-28.5t46-35 44.5-48 28-60.5 12.5-81zm384 192q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 655 B |
3
static/admin/img/icon-viewlink.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#2b70bf" d="M1664 960q-152-236-381-353 61 104 61 225 0 185-131.5 316.5t-316.5 131.5-316.5-131.5-131.5-316.5q0-121 61-225-229 117-381 353 133 205 333.5 326.5t434.5 121.5 434.5-121.5 333.5-326.5zm-720-384q0-20-14-34t-34-14q-125 0-214.5 89.5t-89.5 214.5q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm848 384q0 34-20 69-140 230-376.5 368.5t-499.5 138.5-499.5-139-376.5-368q-20-35-20-69t20-69q140-229 376.5-368t499.5-139 499.5 139 376.5 368q20 35 20 69z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 581 B |
3
static/admin/img/icon-yes.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="13" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#70bf2b" d="M1412 734q0-28-18-46l-91-90q-19-19-45-19t-45 19l-408 407-226-226q-19-19-45-19t-45 19l-91 90q-18 18-18 46 0 27 18 45l362 362q19 19 45 19 27 0 46-19l543-543q18-18 18-45zm252 162q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 436 B |
3
static/admin/img/inline-delete.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="16" height="16" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#999999" d="M1277 1122q0-26-19-45l-181-181 181-181q19-19 19-45 0-27-19-46l-90-90q-19-19-46-19-26 0-45 19l-181 181-181-181q-19-19-45-19-27 0-46 19l-90 90q-19 19-19 46 0 26 19 45l181 181-181 181q-19 19-19 45 0 27 19 46l90 90q19 19 46 19 26 0 45-19l181-181 181 181q19 19 45 19 27 0 46-19l90-90q19-19 19-46zm387-226q0 209-103 385.5t-279.5 279.5-385.5 103-385.5-103-279.5-279.5-103-385.5 103-385.5 279.5-279.5 385.5-103 385.5 103 279.5 279.5 103 385.5z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 560 B |
3
static/admin/img/search.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="15" height="15" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill="#555555" d="M1216 832q0-185-131.5-316.5t-316.5-131.5-316.5 131.5-131.5 316.5 131.5 316.5 316.5 131.5 316.5-131.5 131.5-316.5zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225-55.5-273.5 55.5-273.5 150-225 225-150 273.5-55.5 273.5 55.5 225 150 150 225 55.5 273.5q0 220-124 399l343 343q37 37 37 90z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 458 B |