diff --git a/.gitignore b/.gitignore index 6d83bf0..24a5bc3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.pyc env +venv +siteenv +newenv .vscode diff --git a/administrasi/administrasi/settings.py b/administrasi/administrasi/settings.py index b677dd7..6fdc46f 100644 --- a/administrasi/administrasi/settings.py +++ b/administrasi/administrasi/settings.py @@ -25,7 +25,7 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['*'] # Application definition @@ -38,6 +38,7 @@ 'django.contrib.messages', 'django.contrib.staticfiles', 'surat_menyurat', + 'inventaris', ] MIDDLEWARE = [ @@ -76,12 +77,14 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'django_class_base', - 'USER': 'root', - 'PASSWORD': 'root', - 'HOST': '127.0.0.1', - 'PORT': '8889', + # 'ENGINE': 'django.db.backends.mysql', + # 'NAME': 'django_class_base', + # 'USER': 'root', + # 'PASSWORD': 'root', + # 'HOST': '127.0.0.1', + # 'PORT': '8889', + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } diff --git a/administrasi/administrasi/urls.py b/administrasi/administrasi/urls.py index f677222..dfd90c6 100644 --- a/administrasi/administrasi/urls.py +++ b/administrasi/administrasi/urls.py @@ -1,11 +1,13 @@ from django.contrib import admin from django.urls import path, include from surat_menyurat import views +# from inventaris import views as inven_views urlpatterns = [ path('', views.login_redirect, name='login_redirect'), path('home/', views.home, name='home'), path('surat/', include('surat_menyurat.urls', namespace="surat")), + path('inventaris/', include('inventaris.urls', namespace="inventaris_itec")),#include('inventaris.urls', namespace='inventaris')), path('accounts/', include('django.contrib.auth.urls')), path('admin/', admin.site.urls), ] diff --git a/administrasi/db.sqlite3 b/administrasi/db.sqlite3 new file mode 100644 index 0000000..0aaa8e3 Binary files /dev/null and b/administrasi/db.sqlite3 differ diff --git a/administrasi/inventaris/__init__.py b/administrasi/inventaris/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/administrasi/inventaris/admin.py b/administrasi/inventaris/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/administrasi/inventaris/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/administrasi/inventaris/apps.py b/administrasi/inventaris/apps.py new file mode 100644 index 0000000..a99e1a2 --- /dev/null +++ b/administrasi/inventaris/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class InventarisConfig(AppConfig): + name = 'inventaris' diff --git a/administrasi/inventaris/forms.py b/administrasi/inventaris/forms.py new file mode 100644 index 0000000..29e5a0e --- /dev/null +++ b/administrasi/inventaris/forms.py @@ -0,0 +1,12 @@ +from django import forms +from .models import Inventaris + +class FormInventaris(forms.ModelForm): + class Meta: + model = Inventaris + exclude = ('id',) + + def __init__(self, *args, **kwargs): + super(FormInventaris, self).__init__(*args, **kwargs) + for visible in self.visible_fields(): + visible.field.widget.attrs['class'] = 'form-control form-control-sm' \ No newline at end of file diff --git a/administrasi/inventaris/migrations/0001_initial.py b/administrasi/inventaris/migrations/0001_initial.py new file mode 100644 index 0000000..d08102a --- /dev/null +++ b/administrasi/inventaris/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# Generated by Django 2.1.4 on 2018-12-27 17:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Inventaris', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nomor', models.CharField(error_messages={'unique': 'A student with that nim already exists.'}, max_length=255, unique=True)), + ('nama_barang', models.CharField(max_length=255)), + ('jumlah', models.IntegerField(default=1)), + ('kondisi_barang', models.CharField(choices=[('baik', 'Baik'), ('sedang', 'Sedang'), ('rusak', 'Rusak')], default='baik', max_length=20)), + ], + ), + ] diff --git a/administrasi/inventaris/migrations/__init__.py b/administrasi/inventaris/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/administrasi/inventaris/models.py b/administrasi/inventaris/models.py new file mode 100644 index 0000000..764f66d --- /dev/null +++ b/administrasi/inventaris/models.py @@ -0,0 +1,18 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +# Create your models here. +class Inventaris(models.Model): + KONDISI_BARANG = ( + ('baik', 'Baik'), + ('sedang', 'Sedang'), + ('rusak', 'Rusak'), + ) + nomor = models.CharField(max_length=255, blank=False, null=False, unique=True, + error_messages={ + 'unique': _( + "Nomor Inventaris Sudah ada"), + }) + nama_barang = models.CharField(max_length=255) + jumlah = models.IntegerField(default=1) + kondisi_barang = models.CharField(max_length=20, choices=KONDISI_BARANG, default='baik') \ No newline at end of file diff --git a/administrasi/inventaris/tests.py b/administrasi/inventaris/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/administrasi/inventaris/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/administrasi/inventaris/urls.py b/administrasi/inventaris/urls.py new file mode 100644 index 0000000..6392c86 --- /dev/null +++ b/administrasi/inventaris/urls.py @@ -0,0 +1,11 @@ +from django.urls import path, include +from . import views + +app_name = 'inventaris' + +urlpatterns = [ + path('inventaris-list', views.InventarisListView.as_view(), name='inventaris_list'), + path('inventaris-form', views.InventarisCreateView.as_view(), name='inventaris_form'), + path('inventaris-delete/', views.InventarisDeleteView.as_view(), name='inventaris_delete'), + path('inventaris-edit/', views.InventarisUpdateView.as_view(), name='inventaris_edit'), +] \ No newline at end of file diff --git a/administrasi/inventaris/views.py b/administrasi/inventaris/views.py new file mode 100644 index 0000000..d4b9d0a --- /dev/null +++ b/administrasi/inventaris/views.py @@ -0,0 +1,53 @@ +from django.shortcuts import render, redirect, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.utils.decorators import method_decorator +from django.contrib.auth.mixins import LoginRequiredMixin +from django.views.generic import ListView, CreateView, View, UpdateView +from .models import Inventaris +from .forms import FormInventaris +from django.urls import reverse_lazy + +# Create your views here. + +class InventarisListView(LoginRequiredMixin, ListView): + queryset = Inventaris.objects.all() + template_name = 'inventaris/inventaris_list.html' + +class InventarisCreateView(LoginRequiredMixin, CreateView): + form_class = FormInventaris + template_name = 'inventaris/inventaris_new.html' + success_url = reverse_lazy('inventaris:inventaris_list') + +class InventarisDeleteView(LoginRequiredMixin, View): + def get(self, r, id): + instansi = get_object_or_404(Inventaris, id=id) + instansi.delete() + return redirect ('inventaris:inventaris_list') + +# class InventarisUpdateView(LoginRequiredMixin, UpdateView): +# model = Inventaris +# form_class = FormInventaris +# template_name = 'inventaris/inventaris_edit.html' + +# def form_valid(self, form): +# self.object = form.save(commit=False) +# self.object.save() +# return redirect('inventaris:inventaris_list') + +# @method_decorator(login_required) +# def dispatch(self, request, *args, **kwargs): +# return super(InventarisUpdateView, self).dispatch(request, *args, **kwargs) + +class InventarisUpdateView(LoginRequiredMixin, UpdateView): + model = Inventaris + form_class = FormInventaris + template_name = 'inventaris/inventaris_edit.html' + + def form_valid(self, form): + self.object = form.save(commit=False) + self.object.save() + return redirect("inventaris:inventaris_list") + + @method_decorator(login_required) + def dispatch(self, request, *args, **kwargs): + return super(InventarisUpdateView, self).dispatch(request, *args, **kwargs) \ No newline at end of file diff --git a/administrasi/surat_menyurat/migrations/0001_initial.py b/administrasi/surat_menyurat/migrations/0001_initial.py index 5340af5..a6b7c90 100644 --- a/administrasi/surat_menyurat/migrations/0001_initial.py +++ b/administrasi/surat_menyurat/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.1.4 on 2018-12-22 09:49 +# Generated by Django 2.1.4 on 2018-12-27 17:40 from django.db import migrations, models import django.db.models.deletion diff --git a/administrasi/surat_menyurat/urls.py b/administrasi/surat_menyurat/urls.py index 3247e5a..9461ac3 100644 --- a/administrasi/surat_menyurat/urls.py +++ b/administrasi/surat_menyurat/urls.py @@ -12,10 +12,10 @@ path('suratmasuk-list/', views.SMListView.as_view(), name='sm_list'), path('suratmasuk-form/', views.SMCreateView.as_view(), name='sm_form'), path('suratmasuk-delete/', views.SMDeleteView.as_view(), name='sm_delete'), - path('suratmasuk-edit/', views.SMUpdateView.as_view(), name='sm_edit'), + path('suratmasuk-edit/', views.SMUpdateView.as_view(), name='sm_edit'), path('suratkeluar-list/', views.SKListView.as_view(), name='sk_list'), path('suratkeluar-form/', views.SKCreateView.as_view(), name='sk_form'), path('suratkeluar-delete/', views.SKDeleteView.as_view(), name='sk_delete'), - path('suratkeluaredit/', views.SKUpdateView.as_view(), name='sk_edit'), + path('suratkeluaredit/', views.SKUpdateView.as_view(), name='sk_edit'), ] diff --git a/administrasi/templates/inventaris/inventaris_edit.html b/administrasi/templates/inventaris/inventaris_edit.html new file mode 100644 index 0000000..a18a8bd --- /dev/null +++ b/administrasi/templates/inventaris/inventaris_edit.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block content %} + +
+ {% csrf_token %} + + + {{form.as_table}} +
+ +
+{% endblock %} \ No newline at end of file diff --git a/administrasi/templates/inventaris/inventaris_list.html b/administrasi/templates/inventaris/inventaris_list.html new file mode 100644 index 0000000..135007a --- /dev/null +++ b/administrasi/templates/inventaris/inventaris_list.html @@ -0,0 +1,37 @@ +{% extends 'base.html' %} + +{% block content %} + + + + + + + + + + + + + {% for dt in object_list %} + + + + + + + + + + + {% endfor %} +
NoNomor InventarisNama BarangJumlahKondisi#
{{ forloop.counter }} .{{ dt.nomor }}{{dt.nama_barang}}{{dt.jumlah}}{{dt.kondisi_barang}} + Edit + Hapus +
+{% endblock %} \ No newline at end of file diff --git a/administrasi/templates/inventaris/inventaris_new.html b/administrasi/templates/inventaris/inventaris_new.html new file mode 100644 index 0000000..ce47dd9 --- /dev/null +++ b/administrasi/templates/inventaris/inventaris_new.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block content %} + +
+ {% csrf_token %} + + {{form.as_table}} +
+ +
+{% endblock %} \ No newline at end of file diff --git a/administrasi/templates/navbar.html b/administrasi/templates/navbar.html index 0afafed..e6b15a6 100644 --- a/administrasi/templates/navbar.html +++ b/administrasi/templates/navbar.html @@ -17,6 +17,15 @@ Add new +
  • + {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} + {% trans "History" %} +
  • +{% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif %} +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_list.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_list.html new file mode 100644 index 0000000..0ffca67 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_list.html @@ -0,0 +1,82 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls static admin_list %} + +{% block extrastyle %} + {{ block.super }} + + {% if cl.formset %} + + {% endif %} + {% if cl.formset or action_form %} + + {% endif %} + {{ media.css }} + {% if not actions_on_top and not actions_on_bottom %} + + {% endif %} +{% endblock %} + +{% block extrahead %} +{{ block.super }} +{{ media.js }} +{% endblock %} + +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-list{% endblock %} + +{% if not is_popup %} +{% block breadcrumbs %} + +{% endblock %} +{% endif %} + +{% block coltype %}flex{% endblock %} + +{% block content %} +
    + {% block object-tools %} +
      + {% block object-tools-items %} + {% change_list_object_tools %} + {% endblock %} +
    + {% endblock %} + {% if cl.formset.errors %} +

    + {% if cl.formset.total_error_count == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} +

    + {{ cl.formset.non_form_errors }} + {% endif %} +
    + {% block search %}{% search_form cl %}{% endblock %} + {% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %} + + {% block filters %} + {% if cl.has_filters %} +
    +

    {% trans 'Filter' %}

    + {% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %} +
    + {% endif %} + {% endblock %} + +
    {% csrf_token %} + {% if cl.formset %} +
    {{ cl.formset.management_form }}
    + {% endif %} + + {% block result_list %} + {% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %} + {% result_list cl %} + {% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %} + {% endblock %} + {% block pagination %}{% pagination cl %}{% endblock %} +
    +
    +
    +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_list_object_tools.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_list_object_tools.html new file mode 100644 index 0000000..5d6d458 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_list_object_tools.html @@ -0,0 +1,12 @@ +{% load i18n admin_urls %} + +{% block object-tools-items %} + {% if has_add_permission %} +
  • + {% url cl.opts|admin_urlname:'add' as add_url %} + + {% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %} + +
  • + {% endif %} +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_list_results.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_list_results.html new file mode 100644 index 0000000..b3d7dd0 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/change_list_results.html @@ -0,0 +1,38 @@ +{% load i18n static %} +{% if result_hidden_fields %} +
    {# DIV for HTML validation #} +{% for item in result_hidden_fields %}{{ item }}{% endfor %} +
    +{% endif %} +{% if results %} +
    + + + +{% for header in result_headers %} +{% endfor %} + + + +{% for result in results %} +{% if result.form.non_field_errors %} + +{% endif %} +{% for item in result %}{{ item }}{% endfor %} +{% endfor %} + +
    + {% if header.sortable %} + {% if header.sort_priority > 0 %} +
    + + {% if num_sorted_fields > 1 %}{{ header.sort_priority }}{% endif %} + +
    + {% endif %} + {% endif %} +
    {% if header.sortable %}{{ header.text|capfirst }}{% else %}{{ header.text|capfirst }}{% endif %}
    +
    +
    {{ result.form.non_field_errors }}
    +
    +{% endif %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/date_hierarchy.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/date_hierarchy.html new file mode 100644 index 0000000..65ae800 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/date_hierarchy.html @@ -0,0 +1,16 @@ +{% if show %} +
    +
    +
    +{% endif %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/delete_confirmation.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/delete_confirmation.html new file mode 100644 index 0000000..c28a87c --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/delete_confirmation.html @@ -0,0 +1,52 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls static %} + +{% block extrahead %} + {{ block.super }} + {{ media }} + +{% endblock %} + +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +{% if perms_lacking %} +

    {% blocktrans with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}

    +
      + {% for obj in perms_lacking %} +
    • {{ obj }}
    • + {% endfor %} +
    +{% elif protected %} +

    {% blocktrans with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would require deleting the following protected related objects:{% endblocktrans %}

    +
      + {% for obj in protected %} +
    • {{ obj }}
    • + {% endfor %} +
    +{% else %} +

    {% blocktrans with escaped_object=object %}Are you sure you want to delete the {{ object_name }} "{{ escaped_object }}"? All of the following related items will be deleted:{% endblocktrans %}

    + {% include "admin/includes/object_delete_summary.html" %} +

    {% trans "Objects" %}

    +
      {{ deleted_objects|unordered_list }}
    +
    {% csrf_token %} +
    + + {% if is_popup %}{% endif %} + {% if to_field %}{% endif %} + + {% trans "No, take me back" %} +
    +
    +{% endif %} +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/delete_selected_confirmation.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/delete_selected_confirmation.html new file mode 100644 index 0000000..4d77ae3 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/delete_selected_confirmation.html @@ -0,0 +1,55 @@ +{% extends "admin/base_site.html" %} +{% load i18n l10n admin_urls static %} + +{% block extrahead %} + {{ block.super }} + {{ media }} + +{% endblock %} + +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +{% if perms_lacking %} +

    {% blocktrans %}Deleting the selected {{ objects_name }} would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}

    +
      + {% for obj in perms_lacking %} +
    • {{ obj }}
    • + {% endfor %} +
    +{% elif protected %} +

    {% blocktrans %}Deleting the selected {{ objects_name }} would require deleting the following protected related objects:{% endblocktrans %}

    +
      + {% for obj in protected %} +
    • {{ obj }}
    • + {% endfor %} +
    +{% else %} +

    {% blocktrans %}Are you sure you want to delete the selected {{ objects_name }}? All of the following objects and their related items will be deleted:{% endblocktrans %}

    + {% include "admin/includes/object_delete_summary.html" %} +

    {% trans "Objects" %}

    + {% for deletable_object in deletable_objects %} +
      {{ deletable_object|unordered_list }}
    + {% endfor %} +
    {% csrf_token %} +
    + {% for obj in queryset %} + + {% endfor %} + + + + {% trans "No, take me back" %} +
    +
    +{% endif %} +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/edit_inline/stacked.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/edit_inline/stacked.html new file mode 100644 index 0000000..507f69b --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/edit_inline/stacked.html @@ -0,0 +1,25 @@ +{% load i18n admin_urls static %} +
    +
    +

    {{ inline_admin_formset.opts.verbose_name_plural|capfirst }}

    +{{ inline_admin_formset.formset.management_form }} +{{ inline_admin_formset.formset.non_form_errors }} + +{% for inline_admin_form in inline_admin_formset %}
    +

    {{ inline_admin_formset.opts.verbose_name|capfirst }}: {% if inline_admin_form.original %}{{ inline_admin_form.original }}{% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %} {% if inline_admin_formset.has_change_permission %}{% trans "Change" %}{% else %}{% trans "View" %}{% endif %}{% endif %} +{% else %}#{{ forloop.counter }}{% endif %} + {% if inline_admin_form.show_url %}{% trans "View on site" %}{% endif %} + {% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}{% endif %} +

    + {% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %} + {% for fieldset in inline_admin_form %} + {% include "admin/includes/fieldset.html" %} + {% endfor %} + {% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %} + {{ inline_admin_form.fk_field.field }} +
    {% endfor %} +
    +
    diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/edit_inline/tabular.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/edit_inline/tabular.html new file mode 100644 index 0000000..2584745 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/edit_inline/tabular.html @@ -0,0 +1,75 @@ +{% load i18n admin_urls static admin_modify %} +
    + +
    diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/filter.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/filter.html new file mode 100644 index 0000000..cd88652 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/filter.html @@ -0,0 +1,8 @@ +{% load i18n %} +

    {% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}

    + diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/includes/fieldset.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/includes/fieldset.html new file mode 100644 index 0000000..218fd5a --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/includes/fieldset.html @@ -0,0 +1,29 @@ +
    + {% if fieldset.name %}

    {{ fieldset.name }}

    {% endif %} + {% if fieldset.description %} +
    {{ fieldset.description|safe }}
    + {% endif %} + {% for line in fieldset %} +
    + {% if line.fields|length_is:'1' %}{{ line.errors }}{% endif %} + {% for field in line %} + + {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %} + {% if field.is_checkbox %} + {{ field.field }}{{ field.label_tag }} + {% else %} + {{ field.label_tag }} + {% if field.is_readonly %} +
    {{ field.contents }}
    + {% else %} + {{ field.field }} + {% endif %} + {% endif %} + {% if field.field.help_text %} +
    {{ field.field.help_text|safe }}
    + {% endif %} +
    + {% endfor %} + + {% endfor %} +
    diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/includes/object_delete_summary.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/includes/object_delete_summary.html new file mode 100644 index 0000000..6a8bf65 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/includes/object_delete_summary.html @@ -0,0 +1,7 @@ +{% load i18n %} +

    {% trans "Summary" %}

    +
      + {% for model_name, object_count in model_count %} +
    • {{ model_name|capfirst }}: {{ object_count }}
    • + {% endfor %} +
    diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/index.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/index.html new file mode 100644 index 0000000..2b50015 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/index.html @@ -0,0 +1,86 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %}{{ block.super }}{% endblock %} + +{% block coltype %}colMS{% endblock %} + +{% block bodyclass %}{{ block.super }} dashboard{% endblock %} + +{% block breadcrumbs %}{% endblock %} + +{% block content %} +
    + +{% if app_list %} + {% for app in app_list %} +
    + + + {% for model in app.models %} + + {% if model.admin_url %} + + {% else %} + + {% endif %} + + {% if model.add_url %} + + {% else %} + + {% endif %} + + {% if model.admin_url %} + {% if model.view_only %} + + {% else %} + + {% endif %} + {% else %} + + {% endif %} + + {% endfor %} +
    + {{ app.name }} +
    {{ model.name }}{{ model.name }}{% trans 'Add' %} {% trans 'View' %}{% trans 'Change' %} 
    +
    + {% endfor %} +{% else %} +

    {% trans "You don't have permission to view or edit anything." %}

    +{% endif %} +
    +{% endblock %} + +{% block sidebar %} + +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/invalid_setup.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/invalid_setup.html new file mode 100644 index 0000000..7c71107 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/invalid_setup.html @@ -0,0 +1,13 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +

    {% trans "Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user." %}

    +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/login.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/login.html new file mode 100644 index 0000000..396be27 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/login.html @@ -0,0 +1,66 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %}{{ block.super }} +{{ form.media }} +{% endblock %} + +{% block bodyclass %}{{ block.super }} login{% endblock %} + +{% block usertools %}{% endblock %} + +{% block nav-global %}{% endblock %} + +{% block content_title %}{% endblock %} + +{% block breadcrumbs %}{% endblock %} + +{% block content %} +{% if form.errors and not form.non_field_errors %} +

    +{% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} +

    +{% endif %} + +{% if form.non_field_errors %} +{% for error in form.non_field_errors %} +

    + {{ error }} +

    +{% endfor %} +{% endif %} + +
    + +{% if user.is_authenticated %} +

    +{% blocktrans trimmed %} + You are authenticated as {{ username }}, but are not authorized to + access this page. Would you like to login to a different account? +{% endblocktrans %} +

    +{% endif %} + +
    {% csrf_token %} +
    + {{ form.username.errors }} + {{ form.username.label_tag }} {{ form.username }} +
    +
    + {{ form.password.errors }} + {{ form.password.label_tag }} {{ form.password }} + +
    + {% url 'admin_password_reset' as password_reset_url %} + {% if password_reset_url %} + + {% endif %} +
    + +
    +
    + +
    +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/object_history.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/object_history.html new file mode 100644 index 0000000..f512aa1 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/object_history.html @@ -0,0 +1,42 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_urls %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +
    +
    + +{% if action_list %} + + + + + + + + + + {% for action in action_list %} + + + + + + {% endfor %} + +
    {% trans 'Date/time' %}{% trans 'User' %}{% trans 'Action' %}
    {{ action.action_time|date:"DATETIME_FORMAT" }}{{ action.user.get_username }}{% if action.user.get_full_name %} ({{ action.user.get_full_name }}){% endif %}{{ action.get_change_message }}
    +{% else %} +

    {% trans "This object doesn't have a change history. It probably wasn't added via this admin site." %}

    +{% endif %} +
    +
    +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/pagination.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/pagination.html new file mode 100644 index 0000000..bef843a --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/pagination.html @@ -0,0 +1,12 @@ +{% load admin_list %} +{% load i18n %} +

    +{% if pagination_required %} +{% for i in page_range %} + {% paginator_number cl i %} +{% endfor %} +{% endif %} +{{ cl.result_count }} {% if cl.result_count == 1 %}{{ cl.opts.verbose_name }}{% else %}{{ cl.opts.verbose_name_plural }}{% endif %} +{% if show_all_url %}  {% trans 'Show all' %}{% endif %} +{% if cl.formset and cl.result_count %}{% endif %} +

    diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/popup_response.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/popup_response.html new file mode 100644 index 0000000..6e4fac8 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/popup_response.html @@ -0,0 +1,11 @@ +{% load i18n static %} + + {% trans 'Popup closing...' %} + + + + diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/prepopulated_fields_js.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/prepopulated_fields_js.html new file mode 100644 index 0000000..1572339 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/prepopulated_fields_js.html @@ -0,0 +1,6 @@ +{% load l10n static %} + diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/related_widget_wrapper.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/related_widget_wrapper.html new file mode 100644 index 0000000..658a7b5 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/related_widget_wrapper.html @@ -0,0 +1,35 @@ +{% load i18n static %} + diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/search_form.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/search_form.html new file mode 100644 index 0000000..3bb5cba --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/search_form.html @@ -0,0 +1,16 @@ +{% load i18n static %} +{% if cl.search_fields %} +
    +{% endif %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/submit_line.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/submit_line.html new file mode 100644 index 0000000..b9467e8 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/submit_line.html @@ -0,0 +1,14 @@ +{% load i18n admin_urls %} +
    +{% block submit-row %} +{% if show_save %}{% endif %} +{% if show_delete_link %} + {% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %} + +{% endif %} +{% if show_save_as_new %}{% endif %} +{% if show_save_and_add_another %}{% endif %} +{% if show_save_and_continue %}{% endif %} +{% if show_close %}{% trans 'Close' %}{% endif %} +{% endblock %} +
    diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/clearable_file_input.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/clearable_file_input.html new file mode 100644 index 0000000..80699d1 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/clearable_file_input.html @@ -0,0 +1,6 @@ +{% if widget.is_initial %}

    {{ widget.initial_text }}: {{ widget.value }}{% if not widget.required %} + + +{% endif %}
    +{{ widget.input_text }}:{% endif %} +{% if widget.is_initial %}

    {% endif %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/foreign_key_raw_id.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/foreign_key_raw_id.html new file mode 100644 index 0000000..fa641b7 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/foreign_key_raw_id.html @@ -0,0 +1 @@ +{% include 'django/forms/widgets/input.html' %}{% if related_url %}{% endif %}{% if link_label %} {% if link_url %}{% endif %}{{ link_label }}{% if link_url %}{% endif %}{% endif %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/many_to_many_raw_id.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/many_to_many_raw_id.html new file mode 100644 index 0000000..0dd0331 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/many_to_many_raw_id.html @@ -0,0 +1 @@ +{% include 'admin/widgets/foreign_key_raw_id.html' %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/radio.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/radio.html new file mode 100644 index 0000000..780899a --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/radio.html @@ -0,0 +1 @@ +{% include "django/forms/widgets/multiple_input.html" %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html new file mode 100644 index 0000000..281d331 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/related_widget_wrapper.html @@ -0,0 +1,29 @@ +{% load i18n static %} + diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/split_datetime.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/split_datetime.html new file mode 100644 index 0000000..7fc7bf6 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/split_datetime.html @@ -0,0 +1,4 @@ +

    + {{ date_label }} {% with widget=widget.subwidgets.0 %}{% include widget.template_name %}{% endwith %}
    + {{ time_label }} {% with widget=widget.subwidgets.1 %}{% include widget.template_name %}{% endwith %} +

    diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/url.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/url.html new file mode 100644 index 0000000..ee1a66a --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/admin/widgets/url.html @@ -0,0 +1 @@ +{% if widget.value %}

    {{ current_label }} {{ widget.value }}
    {{ change_label }} {% endif %}{% include "django/forms/widgets/input.html" %}{% if widget.value %}

    {% endif %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/logged_out.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/logged_out.html new file mode 100644 index 0000000..6a18186 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/logged_out.html @@ -0,0 +1,12 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} + +{% block breadcrumbs %}{% endblock %} + +{% block content %} + +

    {% trans "Thanks for spending some quality time with the Web site today." %}

    + +

    {% trans 'Log in again' %}

    + +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_change_done.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_change_done.html new file mode 100644 index 0000000..3e557eb --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_change_done.html @@ -0,0 +1,15 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} +{% block userlinks %}{% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}{% trans 'Documentation' %} / {% endif %}{% trans 'Change password' %} / {% trans 'Log out' %}{% endblock %} +{% block breadcrumbs %} + +{% endblock %} + +{% block title %}{{ title }}{% endblock %} +{% block content_title %}

    {{ title }}

    {% endblock %} +{% block content %} +

    {% trans 'Your password was changed.' %}

    +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_change_form.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_change_form.html new file mode 100644 index 0000000..8c26108 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_change_form.html @@ -0,0 +1,60 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} +{% block extrastyle %}{{ block.super }}{% endblock %} +{% block userlinks %}{% url 'django-admindocs-docroot' as docsroot %}{% if docsroot %}{% trans 'Documentation' %} / {% endif %} {% trans 'Change password' %} / {% trans 'Log out' %}{% endblock %} +{% block breadcrumbs %} + +{% endblock %} + +{% block title %}{{ title }}{% endblock %} +{% block content_title %}

    {{ title }}

    {% endblock %} + +{% block content %}
    + +
    {% csrf_token %} +
    +{% if form.errors %} +

    + {% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} +

    +{% endif %} + + +

    {% trans "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." %}

    + +
    + +
    + {{ form.old_password.errors }} + {{ form.old_password.label_tag }} {{ form.old_password }} +
    + +
    + {{ form.new_password1.errors }} + {{ form.new_password1.label_tag }} {{ form.new_password1 }} + {% if form.new_password1.help_text %} +
    {{ form.new_password1.help_text|safe }}
    + {% endif %} +
    + +
    +{{ form.new_password2.errors }} + {{ form.new_password2.label_tag }} {{ form.new_password2 }} + {% if form.new_password2.help_text %} +
    {{ form.new_password2.help_text|safe }}
    + {% endif %} +
    + +
    + +
    + +
    + +
    +
    + +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_complete.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_complete.html new file mode 100644 index 0000000..19f87a5 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_complete.html @@ -0,0 +1,20 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block title %}{{ title }}{% endblock %} +{% block content_title %}

    {{ title }}

    {% endblock %} + +{% block content %} + +

    {% trans "Your password has been set. You may go ahead and log in now." %}

    + +

    {% trans 'Log in' %}

    + +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_confirm.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_confirm.html new file mode 100644 index 0000000..8666fa9 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_confirm.html @@ -0,0 +1,42 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %}{{ block.super }}{% endblock %} +{% block breadcrumbs %} + +{% endblock %} + +{% block title %}{{ title }}{% endblock %} +{% block content_title %}

    {{ title }}

    {% endblock %} +{% block content %} + +{% if validlink %} + +

    {% trans "Please enter your new password twice so we can verify you typed it in correctly." %}

    + +
    {% csrf_token %} +
    +
    + {{ form.new_password1.errors }} + + {{ form.new_password1 }} +
    +
    + {{ form.new_password2.errors }} + + {{ form.new_password2 }} +
    + +
    +
    + +{% else %} + +

    {% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}

    + +{% endif %} + +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_done.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_done.html new file mode 100644 index 0000000..c6fc358 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_done.html @@ -0,0 +1,19 @@ +{% extends "admin/base_site.html" %} +{% load i18n %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block title %}{{ title }}{% endblock %} +{% block content_title %}

    {{ title }}

    {% endblock %} +{% block content %} + +

    {% trans "We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly." %}

    + +

    {% trans "If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder." %}

    + +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_email.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_email.html new file mode 100644 index 0000000..01b3bcc --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_email.html @@ -0,0 +1,14 @@ +{% load i18n %}{% autoescape off %} +{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %} + +{% trans "Please go to the following page and choose a new password:" %} +{% block reset_link %} +{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} +{% endblock %} +{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }} + +{% trans "Thanks for using our site!" %} + +{% blocktrans %}The {{ site_name }} team{% endblocktrans %} + +{% endautoescape %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_form.html b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_form.html new file mode 100644 index 0000000..5c5d761 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templates/registration/password_reset_form.html @@ -0,0 +1,29 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %}{{ block.super }}{% endblock %} +{% block breadcrumbs %} + +{% endblock %} + +{% block title %}{{ title }}{% endblock %} +{% block content_title %}

    {{ title }}

    {% endblock %} +{% block content %} + +

    {% trans "Forgotten your password? Enter your email address below, and we'll email instructions for setting a new one." %}

    + +
    {% csrf_token %} +
    +
    + {{ form.email.errors }} + + {{ form.email }} +
    + +
    +
    + +{% endblock %} diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/__init__.py b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_list.py b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_list.py new file mode 100644 index 0000000..22dec69 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_list.py @@ -0,0 +1,485 @@ +import datetime + +from django.contrib.admin.templatetags.admin_urls import add_preserved_filters +from django.contrib.admin.utils import ( + display_for_field, display_for_value, label_for_field, lookup_field, +) +from django.contrib.admin.views.main import ( + ALL_VAR, ORDER_VAR, PAGE_VAR, SEARCH_VAR, +) +from django.core.exceptions import ObjectDoesNotExist +from django.db import models +from django.template import Library +from django.template.loader import get_template +from django.templatetags.static import static +from django.urls import NoReverseMatch +from django.utils import formats +from django.utils.html import format_html +from django.utils.safestring import mark_safe +from django.utils.text import capfirst +from django.utils.translation import gettext as _ + +from .base import InclusionAdminNode + +register = Library() + +DOT = '.' + + +@register.simple_tag +def paginator_number(cl, i): + """ + Generate an individual page index link in a paginated list. + """ + if i == DOT: + return '... ' + elif i == cl.page_num: + return format_html('{} ', i + 1) + else: + return format_html('{} ', + cl.get_query_string({PAGE_VAR: i}), + mark_safe(' class="end"' if i == cl.paginator.num_pages - 1 else ''), + i + 1) + + +def pagination(cl): + """ + Generate the series of links to the pages in a paginated list. + """ + paginator, page_num = cl.paginator, cl.page_num + + pagination_required = (not cl.show_all or not cl.can_show_all) and cl.multi_page + if not pagination_required: + page_range = [] + else: + ON_EACH_SIDE = 3 + ON_ENDS = 2 + + # If there are 10 or fewer pages, display links to every page. + # Otherwise, do some fancy + if paginator.num_pages <= 10: + page_range = range(paginator.num_pages) + else: + # Insert "smart" pagination links, so that there are always ON_ENDS + # links at either end of the list of pages, and there are always + # ON_EACH_SIDE links at either end of the "current page" link. + page_range = [] + if page_num > (ON_EACH_SIDE + ON_ENDS): + page_range += [ + *range(0, ON_ENDS), DOT, + *range(page_num - ON_EACH_SIDE, page_num + 1), + ] + else: + page_range.extend(range(0, page_num + 1)) + if page_num < (paginator.num_pages - ON_EACH_SIDE - ON_ENDS - 1): + page_range += [ + *range(page_num + 1, page_num + ON_EACH_SIDE + 1), DOT, + *range(paginator.num_pages - ON_ENDS, paginator.num_pages) + ] + else: + page_range.extend(range(page_num + 1, paginator.num_pages)) + + need_show_all_link = cl.can_show_all and not cl.show_all and cl.multi_page + return { + 'cl': cl, + 'pagination_required': pagination_required, + 'show_all_url': need_show_all_link and cl.get_query_string({ALL_VAR: ''}), + 'page_range': page_range, + 'ALL_VAR': ALL_VAR, + '1': 1, + } + + +@register.tag(name='pagination') +def pagination_tag(parser, token): + return InclusionAdminNode( + parser, token, + func=pagination, + template_name='pagination.html', + takes_context=False, + ) + + +def result_headers(cl): + """ + Generate the list column headers. + """ + ordering_field_columns = cl.get_ordering_field_columns() + for i, field_name in enumerate(cl.list_display): + text, attr = label_for_field( + field_name, cl.model, + model_admin=cl.model_admin, + return_attr=True + ) + is_field_sortable = cl.sortable_by is None or field_name in cl.sortable_by + if attr: + field_name = _coerce_field_name(field_name, i) + # Potentially not sortable + + # if the field is the action checkbox: no sorting and special class + if field_name == 'action_checkbox': + yield { + "text": text, + "class_attrib": mark_safe(' class="action-checkbox-column"'), + "sortable": False, + } + continue + + admin_order_field = getattr(attr, "admin_order_field", None) + if not admin_order_field: + is_field_sortable = False + + if not is_field_sortable: + # Not sortable + yield { + 'text': text, + 'class_attrib': format_html(' class="column-{}"', field_name), + 'sortable': False, + } + continue + + # OK, it is sortable if we got this far + th_classes = ['sortable', 'column-{}'.format(field_name)] + order_type = '' + new_order_type = 'asc' + sort_priority = 0 + # Is it currently being sorted on? + is_sorted = i in ordering_field_columns + if is_sorted: + order_type = ordering_field_columns.get(i).lower() + sort_priority = list(ordering_field_columns).index(i) + 1 + th_classes.append('sorted %sending' % order_type) + new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type] + + # build new ordering param + o_list_primary = [] # URL for making this field the primary sort + o_list_remove = [] # URL for removing this field from sort + o_list_toggle = [] # URL for toggling order type for this field + + def make_qs_param(t, n): + return ('-' if t == 'desc' else '') + str(n) + + for j, ot in ordering_field_columns.items(): + if j == i: # Same column + param = make_qs_param(new_order_type, j) + # We want clicking on this header to bring the ordering to the + # front + o_list_primary.insert(0, param) + o_list_toggle.append(param) + # o_list_remove - omit + else: + param = make_qs_param(ot, j) + o_list_primary.append(param) + o_list_toggle.append(param) + o_list_remove.append(param) + + if i not in ordering_field_columns: + o_list_primary.insert(0, make_qs_param(new_order_type, i)) + + yield { + "text": text, + "sortable": True, + "sorted": is_sorted, + "ascending": order_type == "asc", + "sort_priority": sort_priority, + "url_primary": cl.get_query_string({ORDER_VAR: '.'.join(o_list_primary)}), + "url_remove": cl.get_query_string({ORDER_VAR: '.'.join(o_list_remove)}), + "url_toggle": cl.get_query_string({ORDER_VAR: '.'.join(o_list_toggle)}), + "class_attrib": format_html(' class="{}"', ' '.join(th_classes)) if th_classes else '', + } + + +def _boolean_icon(field_val): + icon_url = static('admin/img/icon-%s.svg' % + {True: 'yes', False: 'no', None: 'unknown'}[field_val]) + return format_html('{}', icon_url, field_val) + + +def _coerce_field_name(field_name, field_index): + """ + Coerce a field_name (which may be a callable) to a string. + """ + if callable(field_name): + if field_name.__name__ == '': + return 'lambda' + str(field_index) + else: + return field_name.__name__ + return field_name + + +def items_for_result(cl, result, form): + """ + Generate the actual list of data. + """ + + def link_in_col(is_first, field_name, cl): + if cl.list_display_links is None: + return False + if is_first and not cl.list_display_links: + return True + return field_name in cl.list_display_links + + first = True + pk = cl.lookup_opts.pk.attname + for field_index, field_name in enumerate(cl.list_display): + empty_value_display = cl.model_admin.get_empty_value_display() + row_classes = ['field-%s' % _coerce_field_name(field_name, field_index)] + try: + f, attr, value = lookup_field(field_name, result, cl.model_admin) + except ObjectDoesNotExist: + result_repr = empty_value_display + else: + empty_value_display = getattr(attr, 'empty_value_display', empty_value_display) + if f is None or f.auto_created: + if field_name == 'action_checkbox': + row_classes = ['action-checkbox'] + boolean = getattr(attr, 'boolean', False) + result_repr = display_for_value(value, empty_value_display, boolean) + if isinstance(value, (datetime.date, datetime.time)): + row_classes.append('nowrap') + else: + if isinstance(f.remote_field, models.ManyToOneRel): + field_val = getattr(result, f.name) + if field_val is None: + result_repr = empty_value_display + else: + result_repr = field_val + else: + result_repr = display_for_field(value, f, empty_value_display) + if isinstance(f, (models.DateField, models.TimeField, models.ForeignKey)): + row_classes.append('nowrap') + if str(result_repr) == '': + result_repr = mark_safe(' ') + row_class = mark_safe(' class="%s"' % ' '.join(row_classes)) + # If list_display_links not defined, add the link tag to the first field + if link_in_col(first, field_name, cl): + table_tag = 'th' if first else 'td' + first = False + + # Display link to the result's change_view if the url exists, else + # display just the result's representation. + try: + url = cl.url_for_result(result) + except NoReverseMatch: + link_or_text = result_repr + else: + url = add_preserved_filters({'preserved_filters': cl.preserved_filters, 'opts': cl.opts}, url) + # Convert the pk to something that can be used in Javascript. + # Problem cases are non-ASCII strings. + if cl.to_field: + attr = str(cl.to_field) + else: + attr = pk + value = result.serializable_value(attr) + link_or_text = format_html( + '{}', + url, + format_html( + ' data-popup-opener="{}"', value + ) if cl.is_popup else '', + result_repr) + + yield format_html('<{}{}>{}', + table_tag, + row_class, + link_or_text, + table_tag) + else: + # By default the fields come from ModelAdmin.list_editable, but if we pull + # the fields out of the form instead of list_editable custom admins + # can provide fields on a per request basis + if (form and field_name in form.fields and not ( + field_name == cl.model._meta.pk.name and + form[cl.model._meta.pk.name].is_hidden)): + bf = form[field_name] + result_repr = mark_safe(str(bf.errors) + str(bf)) + yield format_html('{}', row_class, result_repr) + if form and not form[cl.model._meta.pk.name].is_hidden: + yield format_html('{}', form[cl.model._meta.pk.name]) + + +class ResultList(list): + """ + Wrapper class used to return items in a list_editable changelist, annotated + with the form object for error reporting purposes. Needed to maintain + backwards compatibility with existing admin templates. + """ + def __init__(self, form, *items): + self.form = form + super().__init__(*items) + + +def results(cl): + if cl.formset: + for res, form in zip(cl.result_list, cl.formset.forms): + yield ResultList(form, items_for_result(cl, res, form)) + else: + for res in cl.result_list: + yield ResultList(None, items_for_result(cl, res, None)) + + +def result_hidden_fields(cl): + if cl.formset: + for res, form in zip(cl.result_list, cl.formset.forms): + if form[cl.model._meta.pk.name].is_hidden: + yield mark_safe(form[cl.model._meta.pk.name]) + + +def result_list(cl): + """ + Display the headers and data list together. + """ + headers = list(result_headers(cl)) + num_sorted_fields = 0 + for h in headers: + if h['sortable'] and h['sorted']: + num_sorted_fields += 1 + return {'cl': cl, + 'result_hidden_fields': list(result_hidden_fields(cl)), + 'result_headers': headers, + 'num_sorted_fields': num_sorted_fields, + 'results': list(results(cl))} + + +@register.tag(name='result_list') +def result_list_tag(parser, token): + return InclusionAdminNode( + parser, token, + func=result_list, + template_name='change_list_results.html', + takes_context=False, + ) + + +def date_hierarchy(cl): + """ + Display the date hierarchy for date drill-down functionality. + """ + if cl.date_hierarchy: + field_name = cl.date_hierarchy + year_field = '%s__year' % field_name + month_field = '%s__month' % field_name + day_field = '%s__day' % field_name + field_generic = '%s__' % field_name + year_lookup = cl.params.get(year_field) + month_lookup = cl.params.get(month_field) + day_lookup = cl.params.get(day_field) + + def link(filters): + return cl.get_query_string(filters, [field_generic]) + + if not (year_lookup or month_lookup or day_lookup): + # select appropriate start level + date_range = cl.queryset.aggregate(first=models.Min(field_name), + last=models.Max(field_name)) + if date_range['first'] and date_range['last']: + if date_range['first'].year == date_range['last'].year: + year_lookup = date_range['first'].year + if date_range['first'].month == date_range['last'].month: + month_lookup = date_range['first'].month + + if year_lookup and month_lookup and day_lookup: + day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup)) + return { + 'show': True, + 'back': { + 'link': link({year_field: year_lookup, month_field: month_lookup}), + 'title': capfirst(formats.date_format(day, 'YEAR_MONTH_FORMAT')) + }, + 'choices': [{'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT'))}] + } + elif year_lookup and month_lookup: + days = getattr(cl.queryset, 'dates')(field_name, 'day') + return { + 'show': True, + 'back': { + 'link': link({year_field: year_lookup}), + 'title': str(year_lookup) + }, + 'choices': [{ + 'link': link({year_field: year_lookup, month_field: month_lookup, day_field: day.day}), + 'title': capfirst(formats.date_format(day, 'MONTH_DAY_FORMAT')) + } for day in days] + } + elif year_lookup: + months = getattr(cl.queryset, 'dates')(field_name, 'month') + return { + 'show': True, + 'back': { + 'link': link({}), + 'title': _('All dates') + }, + 'choices': [{ + 'link': link({year_field: year_lookup, month_field: month.month}), + 'title': capfirst(formats.date_format(month, 'YEAR_MONTH_FORMAT')) + } for month in months] + } + else: + years = getattr(cl.queryset, 'dates')(field_name, 'year') + return { + 'show': True, + 'choices': [{ + 'link': link({year_field: str(year.year)}), + 'title': str(year.year), + } for year in years] + } + + +@register.tag(name='date_hierarchy') +def date_hierarchy_tag(parser, token): + return InclusionAdminNode( + parser, token, + func=date_hierarchy, + template_name='date_hierarchy.html', + takes_context=False, + ) + + +def search_form(cl): + """ + Display a search form for searching the list. + """ + return { + 'cl': cl, + 'show_result_count': cl.result_count != cl.full_result_count, + 'search_var': SEARCH_VAR + } + + +@register.tag(name='search_form') +def search_form_tag(parser, token): + return InclusionAdminNode(parser, token, func=search_form, template_name='search_form.html', takes_context=False) + + +@register.simple_tag +def admin_list_filter(cl, spec): + tpl = get_template(spec.template) + return tpl.render({ + 'title': spec.title, + 'choices': list(spec.choices(cl)), + 'spec': spec, + }) + + +def admin_actions(context): + """ + Track the number of times the action field has been rendered on the page, + so we know which value to use. + """ + context['action_index'] = context.get('action_index', -1) + 1 + return context + + +@register.tag(name='admin_actions') +def admin_actions_tag(parser, token): + return InclusionAdminNode(parser, token, func=admin_actions, template_name='actions.html') + + +@register.tag(name='change_list_object_tools') +def change_list_object_tools_tag(parser, token): + """Display the row of change list object tools.""" + return InclusionAdminNode( + parser, token, + func=lambda context: context, + template_name='change_list_object_tools.html', + ) diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_modify.py b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_modify.py new file mode 100644 index 0000000..60bc560 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_modify.py @@ -0,0 +1,111 @@ +import json + +from django import template +from django.template.context import Context + +from .base import InclusionAdminNode + +register = template.Library() + + +def prepopulated_fields_js(context): + """ + Create a list of prepopulated_fields that should render Javascript for + the prepopulated fields for both the admin form and inlines. + """ + prepopulated_fields = [] + if 'adminform' in context: + prepopulated_fields.extend(context['adminform'].prepopulated_fields) + if 'inline_admin_formsets' in context: + for inline_admin_formset in context['inline_admin_formsets']: + for inline_admin_form in inline_admin_formset: + if inline_admin_form.original is None: + prepopulated_fields.extend(inline_admin_form.prepopulated_fields) + + prepopulated_fields_json = [] + for field in prepopulated_fields: + prepopulated_fields_json.append({ + "id": "#%s" % field["field"].auto_id, + "name": field["field"].name, + "dependency_ids": ["#%s" % dependency.auto_id for dependency in field["dependencies"]], + "dependency_list": [dependency.name for dependency in field["dependencies"]], + "maxLength": field["field"].field.max_length or 50, + "allowUnicode": getattr(field["field"].field, "allow_unicode", False) + }) + + context.update({ + 'prepopulated_fields': prepopulated_fields, + 'prepopulated_fields_json': json.dumps(prepopulated_fields_json), + }) + return context + + +@register.tag(name='prepopulated_fields_js') +def prepopulated_fields_js_tag(parser, token): + return InclusionAdminNode(parser, token, func=prepopulated_fields_js, template_name="prepopulated_fields_js.html") + + +def submit_row(context): + """ + Display the row of buttons for delete and save. + """ + add = context['add'] + change = context['change'] + is_popup = context['is_popup'] + save_as = context['save_as'] + show_save = context.get('show_save', True) + show_save_and_continue = context.get('show_save_and_continue', True) + has_add_permission = context['has_add_permission'] + has_change_permission = context['has_change_permission'] + has_view_permission = context['has_view_permission'] + has_editable_inline_admin_formsets = context['has_editable_inline_admin_formsets'] + can_save = (has_change_permission and change) or (has_add_permission and add) or has_editable_inline_admin_formsets + can_save_and_continue = not is_popup and can_save and has_view_permission and show_save_and_continue + can_change = has_change_permission or has_editable_inline_admin_formsets + ctx = Context(context) + ctx.update({ + 'can_change': can_change, + 'show_delete_link': ( + not is_popup and context['has_delete_permission'] and + change and context.get('show_delete', True) + ), + 'show_save_as_new': not is_popup and has_change_permission and change and save_as, + 'show_save_and_add_another': ( + has_add_permission and not is_popup and + (not save_as or add) and can_save + ), + 'show_save_and_continue': can_save_and_continue, + 'show_save': show_save and can_save, + 'show_close': not(show_save and can_save) + }) + return ctx + + +@register.tag(name='submit_row') +def submit_row_tag(parser, token): + return InclusionAdminNode(parser, token, func=submit_row, template_name='submit_line.html') + + +@register.tag(name='change_form_object_tools') +def change_form_object_tools_tag(parser, token): + """Display the row of change form object tools.""" + return InclusionAdminNode( + parser, token, + func=lambda context: context, + template_name='change_form_object_tools.html', + ) + + +@register.filter +def cell_count(inline_admin_form): + """Return the number of cells used in a tabular inline.""" + count = 1 # Hidden cell with hidden 'id' field + for fieldset in inline_admin_form: + # Loop through all the fields (one per cell) + for line in fieldset: + for field in line: + count += 1 + if inline_admin_form.formset.can_delete: + # Delete checkbox + count += 1 + return count diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_static.py b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_static.py new file mode 100644 index 0000000..6b1738d --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_static.py @@ -0,0 +1,16 @@ +import warnings + +from django.template import Library +from django.templatetags.static import static as _static +from django.utils.deprecation import RemovedInDjango30Warning + +register = Library() + + +@register.simple_tag +def static(path): + warnings.warn( + '{% load admin_static %} is deprecated in favor of {% load static %}.', + RemovedInDjango30Warning, + ) + return _static(path) diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_urls.py b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_urls.py new file mode 100644 index 0000000..f817c25 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/admin_urls.py @@ -0,0 +1,56 @@ +from urllib.parse import parse_qsl, unquote, urlparse, urlunparse + +from django import template +from django.contrib.admin.utils import quote +from django.urls import Resolver404, get_script_prefix, resolve +from django.utils.http import urlencode + +register = template.Library() + + +@register.filter +def admin_urlname(value, arg): + return 'admin:%s_%s_%s' % (value.app_label, value.model_name, arg) + + +@register.filter +def admin_urlquote(value): + return quote(value) + + +@register.simple_tag(takes_context=True) +def add_preserved_filters(context, url, popup=False, to_field=None): + opts = context.get('opts') + preserved_filters = context.get('preserved_filters') + + parsed_url = list(urlparse(url)) + parsed_qs = dict(parse_qsl(parsed_url[4])) + merged_qs = {} + + if opts and preserved_filters: + preserved_filters = dict(parse_qsl(preserved_filters)) + + match_url = '/%s' % unquote(url).partition(get_script_prefix())[2] + try: + match = resolve(match_url) + except Resolver404: + pass + else: + current_url = '%s:%s' % (match.app_name, match.url_name) + changelist_url = 'admin:%s_%s_changelist' % (opts.app_label, opts.model_name) + if changelist_url == current_url and '_changelist_filters' in preserved_filters: + preserved_filters = dict(parse_qsl(preserved_filters['_changelist_filters'])) + + merged_qs.update(preserved_filters) + + if popup: + from django.contrib.admin.options import IS_POPUP_VAR + merged_qs[IS_POPUP_VAR] = 1 + if to_field: + from django.contrib.admin.options import TO_FIELD_VAR + merged_qs[TO_FIELD_VAR] = to_field + + merged_qs.update(parsed_qs) + + parsed_url[4] = urlencode(merged_qs) + return urlunparse(parsed_url) diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/base.py b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/base.py new file mode 100644 index 0000000..ec6f103 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/base.py @@ -0,0 +1,33 @@ +from inspect import getfullargspec + +from django.template.library import InclusionNode, parse_bits + + +class InclusionAdminNode(InclusionNode): + """ + Template tag that allows its template to be overridden per model, per app, + or globally. + """ + + def __init__(self, parser, token, func, template_name, takes_context=True): + self.template_name = template_name + params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(func) + bits = token.split_contents() + args, kwargs = parse_bits( + parser, bits[1:], params, varargs, varkw, defaults, kwonly, + kwonly_defaults, takes_context, bits[0], + ) + super().__init__(func, takes_context, args, kwargs, filename=None) + + def render(self, context): + opts = context['opts'] + app_label = opts.app_label.lower() + object_name = opts.object_name.lower() + # Load template for this render call. (Setting self.filename isn't + # thread-safe.) + context.render_context[self] = context.template.engine.select_template([ + 'admin/%s/%s/%s' % (app_label, object_name, self.template_name), + 'admin/%s/%s' % (app_label, self.template_name), + 'admin/%s' % (self.template_name,), + ]) + return super().render(context) diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/log.py b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/log.py new file mode 100644 index 0000000..08c2345 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/templatetags/log.py @@ -0,0 +1,59 @@ +from django import template +from django.contrib.admin.models import LogEntry + +register = template.Library() + + +class AdminLogNode(template.Node): + def __init__(self, limit, varname, user): + self.limit, self.varname, self.user = limit, varname, user + + def __repr__(self): + return "" + + def render(self, context): + if self.user is None: + entries = LogEntry.objects.all() + else: + user_id = self.user + if not user_id.isdigit(): + user_id = context[self.user].pk + entries = LogEntry.objects.filter(user__pk=user_id) + context[self.varname] = entries.select_related('content_type', 'user')[:int(self.limit)] + return '' + + +@register.tag +def get_admin_log(parser, token): + """ + Populate a template variable with the admin log for the given criteria. + + Usage:: + + {% get_admin_log [limit] as [varname] for_user [context_var_containing_user_obj] %} + + Examples:: + + {% get_admin_log 10 as admin_log for_user 23 %} + {% get_admin_log 10 as admin_log for_user user %} + {% get_admin_log 10 as admin_log %} + + Note that ``context_var_containing_user_obj`` can be a hard-coded integer + (user ID) or the name of a template context variable containing the user + object whose ID you want. + """ + tokens = token.contents.split() + if len(tokens) < 4: + raise template.TemplateSyntaxError( + "'get_admin_log' statements require two arguments") + if not tokens[1].isdigit(): + raise template.TemplateSyntaxError( + "First argument to 'get_admin_log' must be an integer") + if tokens[2] != 'as': + raise template.TemplateSyntaxError( + "Second argument to 'get_admin_log' must be 'as'") + if len(tokens) > 4: + if tokens[4] != 'for_user': + raise template.TemplateSyntaxError( + "Fourth argument to 'get_admin_log' must be 'for_user'") + return AdminLogNode(limit=tokens[1], varname=tokens[3], user=(tokens[5] if len(tokens) > 5 else None)) diff --git a/sitenv/lib/python3.7/site-packages/django/contrib/admin/tests.py b/sitenv/lib/python3.7/site-packages/django/contrib/admin/tests.py new file mode 100644 index 0000000..f623e66 --- /dev/null +++ b/sitenv/lib/python3.7/site-packages/django/contrib/admin/tests.py @@ -0,0 +1,184 @@ +from django.contrib.staticfiles.testing import StaticLiveServerTestCase +from django.test import modify_settings +from django.test.selenium import SeleniumTestCase +from django.utils.deprecation import MiddlewareMixin +from django.utils.translation import gettext as _ + + +class CSPMiddleware(MiddlewareMixin): + """The admin's JavaScript should be compatible with CSP.""" + def process_response(self, request, response): + response['Content-Security-Policy'] = "default-src 'self'" + return response + + +@modify_settings(MIDDLEWARE={'append': 'django.contrib.admin.tests.CSPMiddleware'}) +class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): + + available_apps = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + ] + + def wait_until(self, callback, timeout=10): + """ + Block the execution of the tests until the specified callback returns a + value that is not falsy. This method can be called, for example, after + clicking a link or submitting a form. See the other public methods that + call this function for more details. + """ + from selenium.webdriver.support.wait import WebDriverWait + WebDriverWait(self.selenium, timeout).until(callback) + + def wait_for_popup(self, num_windows=2, timeout=10): + """ + Block until `num_windows` are present (usually 2, but can be + overridden in the case of pop-ups opening other pop-ups). + """ + self.wait_until(lambda d: len(d.window_handles) == num_windows, timeout) + + def wait_for(self, css_selector, timeout=10): + """ + Block until a CSS selector is found on the page. + """ + from selenium.webdriver.common.by import By + from selenium.webdriver.support import expected_conditions as ec + self.wait_until( + ec.presence_of_element_located((By.CSS_SELECTOR, css_selector)), + timeout + ) + + def wait_for_text(self, css_selector, text, timeout=10): + """ + Block until the text is found in the CSS selector. + """ + from selenium.webdriver.common.by import By + from selenium.webdriver.support import expected_conditions as ec + self.wait_until( + ec.text_to_be_present_in_element( + (By.CSS_SELECTOR, css_selector), text), + timeout + ) + + def wait_for_value(self, css_selector, text, timeout=10): + """ + Block until the value is found in the CSS selector. + """ + from selenium.webdriver.common.by import By + from selenium.webdriver.support import expected_conditions as ec + self.wait_until( + ec.text_to_be_present_in_element_value( + (By.CSS_SELECTOR, css_selector), text), + timeout + ) + + def wait_until_visible(self, css_selector, timeout=10): + """ + Block until the element described by the CSS selector is visible. + """ + from selenium.webdriver.common.by import By + from selenium.webdriver.support import expected_conditions as ec + self.wait_until( + ec.visibility_of_element_located((By.CSS_SELECTOR, css_selector)), + timeout + ) + + def wait_until_invisible(self, css_selector, timeout=10): + """ + Block until the element described by the CSS selector is invisible. + """ + from selenium.webdriver.common.by import By + from selenium.webdriver.support import expected_conditions as ec + self.wait_until( + ec.invisibility_of_element_located((By.CSS_SELECTOR, css_selector)), + timeout + ) + + def wait_page_loaded(self): + """ + Block until page has started to load. + """ + from selenium.common.exceptions import TimeoutException + try: + # Wait for the next page to be loaded + self.wait_for('body') + except TimeoutException: + # IE7 occasionally returns an error "Internet Explorer cannot + # display the webpage" and doesn't load the next page. We just + # ignore it. + pass + + def admin_login(self, username, password, login_url='/admin/'): + """ + Log in to the admin. + """ + self.selenium.get('%s%s' % (self.live_server_url, login_url)) + username_input = self.selenium.find_element_by_name('username') + username_input.send_keys(username) + password_input = self.selenium.find_element_by_name('password') + password_input.send_keys(password) + login_text = _('Log in') + self.selenium.find_element_by_xpath( + '//input[@value="%s"]' % login_text).click() + self.wait_page_loaded() + + def get_css_value(self, selector, attribute): + """ + Return the value for the CSS attribute of a DOM element specified by + the given selector. Uses the jQuery that ships with Django. + """ + return self.selenium.execute_script( + 'return django.jQuery("%s").css("%s")' % (selector, attribute)) + + def get_select_option(self, selector, value): + """ + Return the