1
0
포크 0

Created Django template

This commit is contained in:
Jim Martens 2020-02-18 15:26:39 +01:00
부모 39f2ac6781
커밋 645a4a459c
41개의 변경된 파일14592개의 추가작업 그리고 2개의 파일을 삭제

파일 보기

@ -1,3 +1,9 @@
# django-template
# Django template
Template repository for Django applications
## ToDos
- change ``app``into something more appropriate wherever you find it
- change ``Django template`` and ``DjangoTemplate`` into something more appropriate wherever you find it
Template repository for Django applications

2
app/__init__.py Normal file
파일 보기

@ -0,0 +1,2 @@
# coding=utf-8
default_app_config = 'chaos_dating.apps.ChaosDatingConfig'

39
app/account_urls.py Normal file
파일 보기

@ -0,0 +1,39 @@
# coding=utf-8
from django.contrib.auth import views as auth_views
from django.urls import path
from chaos_dating import views as chaos_views
extra_context = {
'site': {
'title': 'Django template'
},
}
urlpatterns = [
path('login/', chaos_views.user_login, name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('register/', chaos_views.register, name='register'),
path('password_change/',
auth_views.PasswordChangeView.as_view(extra_context=extra_context),
name='password_change'),
path('password_change/done/',
chaos_views.password_change_done,
name='password_change_done'),
path('password_reset/',
auth_views.PasswordResetView.as_view(extra_context=extra_context),
name='password_reset'),
path('password_reset/done/',
auth_views.PasswordResetDoneView.as_view(extra_context=extra_context),
name='password_reset_done'),
path('reset/<uidb64>/<token>/',
auth_views.PasswordResetConfirmView.as_view(extra_context=extra_context),
name='password_reset_confirm'),
path('reset/done/',
auth_views.PasswordResetCompleteView.as_view(extra_context=extra_context),
name='password_reset_complete'),
path('edit_profile/', chaos_views.edit_profile, name='edit_profile'),
]

20
app/admin.py Normal file
파일 보기

@ -0,0 +1,20 @@
# coding=utf-8
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from app.models import Profile
# Register your models here.
admin.site.unregister(User)
class ProfileInline(admin.StackedInline):
model = Profile
class UserProfileAdmin(UserAdmin):
inlines = [ProfileInline, ]
admin.site.register(User, UserProfileAdmin)

7
app/apps.py Normal file
파일 보기

@ -0,0 +1,7 @@
# coding=utf-8
from django.apps import AppConfig
class DjangoTemplateConfig(AppConfig):
name = 'app'
verbose_name = 'Django template app'

44
app/forms.py Normal file
파일 보기

@ -0,0 +1,44 @@
# coding=utf-8
from gettext import gettext as _
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.forms import UsernameField
from django.contrib.auth.models import User
from django.urls import reverse
from app.models import Profile
class UserForm(forms.ModelForm):
password = ReadOnlyPasswordHashField(
label=_("Password"),
help_text=_(
'Raw passwords are not stored, so there is no way to see the '
'password, but you can change the password using '
'<a href="{}">this form</a>.'
),
)
class Meta:
model = User
fields = ['username', 'password', 'email', 'first_name', 'last_name']
field_classes = {'username': UsernameField}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
password = self.fields.get('password')
if password:
password.help_text = password.help_text.format(reverse('password_change'))
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial.get('password')
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile

파일 보기

@ -0,0 +1 @@
# coding=utf-8

11
app/models.py Normal file
파일 보기

@ -0,0 +1,11 @@
# coding=utf-8
from django.contrib.auth.models import User
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(User, models.CASCADE)
def __str__(self):
return self.user.username

파일 보기

파일 보기

@ -0,0 +1,60 @@
{% extends "base.html" %}
{% load i18n %}
{% block header %}
<nav id="mainNavbar" class="navbar navbar-expand-lg navbar-light fixed-top">
<div class="container">
<a class="navbar-brand" href="{% url "chaos_dating:index" %}">
<span class="fal fa-comment-alt-dots d-inline-block align-top"></span>
{{ site.title }}
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
{% if request.user.is_authenticated %}
<li class="nav-item{% if active == "edit_profile" %} active{% endif %}">
<a class="nav-link" href="{% url "edit_profile" %}">
{% trans "Edit Profile" %}{% if active == "edit_profile" %}
<span class="sr-only">(current)</span>
{% endif %}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url "logout" %}">
{% trans "Logout" %}
</a>
</li>
{% else %}
<li class="nav-item{% if active == "login" %} active{% endif %}">
<a class="nav-link" href="{% url "login" %}">
{% trans "Login" %}{% if active == "login" %}
<span class="sr-only">(current)</span>
{% endif %}
</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
{% endblock %}
{% block footer %}
<div class="col">
<nav>
<ul class="nav">
<li class="nav-item">
<a class="nav-link" href="{% url "chaos_dating:legal" %}">{% trans "Legal Notice" %}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url "chaos_dating:privacy" %}">{% trans "Privacy Policy" %}</a>
</li>
</ul>
</nav>
</div>
{% endblock %}

파일 보기

@ -0,0 +1,9 @@
{% extends "app/base.html" %}
{% load i18n %}
{% block title %}{{ site.title }} - {% trans "Home" %}{% endblock %}
{% block content %}
<h1>{% trans "Home" %}</h1>
{% endblock %}

파일 보기

@ -0,0 +1,38 @@
{% extends "app/base.html" %}
{% load i18n %}
{% block title %}{{ site.title }}{% endblock %}
{% block content %}
<h1>{{ site.title }}</h1>
<h2>{% trans "Everything else is crap" %}</h2>
<p>
{% blocktrans %}
Lorem ipsum
{% endblocktrans %}
</p>
<h2>{% trans "Our radical new approach" %}</h2>
<p>
{% blocktrans %}
Lorem ipsum
{% endblocktrans %}
</p>
<p>
{% blocktrans %}
Lorem ipsum
{% endblocktrans %}
</p>
<p>
{% blocktrans %}
Lorem ipsum
{% endblocktrans %}
</p>
<a class="btn btn-primary" href="{% url "register" %}">{% trans "Join the community" %}</a>
{% endblock %}

파일 보기

@ -0,0 +1,48 @@
{% extends "app/base.html" %}
{% load i18n %}
{% block title %}{{ site.title }} - {% trans "Legal Notice" %}{% endblock %}
{% block content %}
<h1>FIXME</h1>
<h1>{% trans "Legal Notice" %}</h1>
{% blocktrans %}
<h2>Information provided according to Sec. 5 German Telemedia Act (TMG):</h2>
<p>Jim Richard Martens<br />
Bindfeldweg 47d<br />
22459 Hamburg</p>
<h2>Contact:</h2>
<p>Email: admin@2martens.de</p>
<h2>Responsible for contents acc. to Sec. 55, para. 2 German Federal Broadcasting Agreement (RstV):</h2>
<p>Jim Martens<br />
Bindfeldweg 47d<br />
22459 Hamburg</p>
<h2>Liability for Contents</h2>
<p>As service providers, we are liable for own contents of these websites according to Sec. 7, paragraph 1 German
Telemedia Act (TMG). However, according to Sec. 8 to 10 German Telemedia Act (TMG), service providers are not
obligated to permanently monitor submitted or stored information or to search for evidences that indicate illegal
activities.</p> <p>Legal obligations to removing information or to blocking the use of information remain unchallenged.
In this case, liability is only possible at the time of knowledge about a specific violation of law. Illegal contents
will be removed immediately at the time we get knowledge of them.</p>
<h2>Liability for Links</h2>
<p>Our offer includes links to external third party websites. We have no influence on the contents of those websites,
therefore we cannot guarantee for those contents. Providers or administrators of linked websites are always responsible
for their own contents.</p>
<p>The linked websites had been checked for possible violations of law at the time of the establishment of the link.
Illegal contents were not detected at the time of the linking. A permanent monitoring of the contents of linked
websites cannot be imposed without reasonable indications that there has been a violation of law. Illegal links
will be removed immediately at the time we get knowledge of them.</p>
<h2>Copyright</h2>
<p>Contents and compilations published on these websites by the providers are subject to German copyright laws.
Reproduction, editing, distribution as well as the use of any kind outside the scope of the copyright law require a
written permission of the author or originator. Downloads and copies of these websites are permitted for private use
only.<br /> The commercial use of our contents without permission of the originator is prohibited.</p>
<p>Copyright laws of third parties are respected as long as the contents on these websites do not originate from the
provider. Contributions of third parties on this site are indicated as such. However, if you notice any violations of
copyright law, please inform us. Such contents will be removed immediately.</p>
<p> </p>
{% endblocktrans %}
{% endblock %}

파일 보기

@ -0,0 +1,88 @@
{% extends "app/base.html" %}
{% load i18n %}
{% block title %}{{ site.title }} - {% trans "Privacy Policy" %}{% endblock %}
{% block content %}
<h1>FIXME</h1>
<h1>{% trans "Privacy Policy" %}</h1>
{% blocktrans %}
<h2>1. An overview of data protection</h2>
<h3>General</h3>
<p>The following gives a simple overview of what happens to your personal information when you visit our website.
Personal information is any data with which you could be personally identified. Detailed information on the subject
of data protection can be found in our privacy policy found below.</p>
<h3>Data collection on our website</h3>
<p><strong>Who is responsible for the data collection on this website?</strong></p>
<p>The data collected on this website are processed by the website operator. The operator's contact details can be found
in the website's required legal notice.</p>
<p><strong>How do we collect your data?</strong></p>
<p>Some data are collected when you provide it to us. This could, for example, be data you enter on a contact form.</p>
<p>Other data are collected automatically by our IT systems when you visit the website. These data are primarily technical
data such as the browser and operating system you are using or when you accessed the page. These data are collected
automatically as soon as you enter our website.</p>
<p><strong>What do we use your data for?</strong></p>
<p>Part of the data is collected to ensure the proper functioning of the website. Other data can be used to analyze how
visitors use the site.</p>
<p><strong>What rights do you have regarding your data?</strong></p>
<p>You always have the right to request information about your stored data, its origin, its recipients, and the purpose
of its collection at no charge. You also have the right to request that it be corrected, blocked, or deleted. You can
contact us at any time using the address given in the legal notice if you have further questions about the issue of
privacy and data protection. You may also, of course, file a complaint with the competent regulatory authorities.</p>
<h2>2. General information and mandatory information</h2>
<h3>Data protection</h3>
<p>The operators of this website take the protection of your personal data very seriously. We treat your personal data as
confidential and in accordance with the statutory data protection regulations and this privacy policy.</p>
<p>If you use this website, various pieces of personal data will be collected. Personal information is any data with which
you could be personally identified. This privacy policy explains what information we collect and what we use it for.
It also explains how and for what purpose this happens.</p>
<p>Please note that data transmitted via the internet (e.g. via email communication) may be subject to security breaches.
Complete protection of your data from third-party access is not possible.</p>
<h3>Notice concerning the party responsible for this website</h3>
<p>The party responsible for processing data on this website is:</p>
<p>Jim Martens<br />
Bindfeldweg 47d<br />
22459 Hamburg</p>
<p>Telephone: +49 40 55504288<br />
Email: admin@2martens.de</p>
<p>The responsible party is the natural or legal person who alone or jointly with others decides on the purposes and means
of processing personal data (names, email addresses, etc.).</p>
<h3>SSL or TLS encryption</h3> <p>This site uses SSL or TLS encryption for security reasons and for the protection of the
transmission of confidential content, such as the inquiries you send to us as the site operator. You can recognize an
encrypted connection in your browser's address line when it changes from "http://" to "https://" and the lock icon is
displayed in your browser's address bar.</p>
<p>If SSL or TLS encryption is activated, the data you transfer to us cannot be read by third parties.</p>
<h3>Opposition to promotional emails</h3> <p>We hereby expressly prohibit the use of contact data published in the context
of website legal notice requirements with regard to sending promotional and informational materials not expressly
requested. The website operator reserves the right to take specific legal action if unsolicited advertising material,
such as email spam, is received.</p>
<h2>3. Data collection on our website</h2>
<h3>Server log files</h3>
<p>The website provider automatically collects and stores information that your browser automatically transmits to us in
"server log files". These are:</p>
<ul>
<li>Browser type and browser version</li>
<li>Operating system used</li>
<li>Referrer URL</li>
<li>Host name of the accessing computer</li>
<li>Time of the server request</li>
<li>IP address</li>
</ul>
<p>These data will not be combined with data from other sources.</p>
<p>The basis for data processing is Art. 6 (1) (b) GDPR, which allows the processing of data to fulfill a contract or
for measures preliminary to a contract.</p>
<h2>4. Plugins and tools</h2>
<h3>YouTube</h3>
<p>Our website uses plugins from YouTube, which is operated by Google. The operator of the pages is
YouTube LLC, 901 Cherry Ave., San Bruno, CA 94066, USA.</p>
<p>If you visit one of our pages featuring a YouTube plugin, a connection to the YouTube servers is established. Here
the YouTube server is informed about which of our pages you have visited.</p>
<p>If you're logged in to your YouTube account, YouTube allows you to associate your browsing behavior directly with your
personal profile. You can prevent this by logging out of your YouTube account.</p>
<p>YouTube is used to help make our website appealing. This constitutes a justified interest pursuant to
Art. 6 (1) (f) GDPR.</p>
<p>Further information about handling user data, can be found in the data protection declaration of YouTube under
<a href="https://www.google.de/intl/en/policies/privacy" target="_blank">https://www.google.de/intl/en/policies/privacy</a>.</p>
{% endblocktrans %}
{% endblock %}

파일 보기

@ -0,0 +1,17 @@
{% extends "app/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{{ site.title }} - {{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<form method="post" action="{% url "edit_profile" %}">
{% csrf_token %}
{{ user_form|crispy }}
{{ profile_form|crispy }}
<button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
</form>
{% endblock %}

파일 보기

@ -0,0 +1,17 @@
{% extends "app/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{{ site.title }} - {{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<form method="post" action="{% url "login" %}">
{% csrf_token %}
{{ form|crispy }}
<input type="hidden" name="next" value="{{ next }}" />
<button type="submit" class="btn btn-primary">{% trans "Login" %}</button>
<a href="{% url "password_reset" %}">{% trans "Forgot password?" %}</a>
</form>
{% endblock %}

파일 보기

@ -0,0 +1,15 @@
{% extends "app/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{{ site_name }} - {{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">{% trans "Change password" %}</button>
</form>
{% endblock %}

파일 보기

@ -0,0 +1,13 @@
{% extends "app/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{{ site.title }} - {{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<p>{% trans "Your password has been set. You may go ahead and log in now." %}</p>
<p><a href="{{ login_url }}">{% trans 'Log in' %}</a></p>
{% endblock %}

파일 보기

@ -0,0 +1,19 @@
{% extends "app/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{{ site.title }} - {{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
{% if validlink %}
<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
<form method="post" action="{% url "password_reset" %}">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">{% trans "Change my password" %}</button>
</form>
{% else %}
<p>{% trans "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}</p>
{% endif %}
{% endblock %}

파일 보기

@ -0,0 +1,13 @@
{% extends "app/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{{ site.title }} - {{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<p>{% trans 'We’ve emailed you instructions for setting your password. If an account exists with the email you entered you should receive them shortly.' %}</p>
<p>{% trans 'If you don’t receive an email, please make sure you’ve entered the address you registered with, and check your spam folder.' %}</p>
{% endblock %}

파일 보기

@ -0,0 +1,17 @@
{% extends "app/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{{ site.title }} - {{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<p>{% trans 'Forgotten your password? Enter your email address below, and we’ll email instructions for setting a new one.' %}</p>
<form method="post" action="{% url "password_reset" %}">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">{% trans "Password reset" %}</button>
</form>
{% endblock %}

파일 보기

@ -0,0 +1,16 @@
{% extends "app/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{{ site.title }} - {% trans "Register" %}{% endblock %}
{% block content %}
<h1>{% trans "Register a new user" %}</h1>
<form method="post" action="{% url "register" %}">
{% csrf_token %}
{{ user_form|crispy }}
{{ profile_form|crispy }}
<button type="submit" class="btn btn-primary">{% trans "Register" %}</button>
</form>
{% endblock %}

5
app/tests.py Normal file
파일 보기

@ -0,0 +1,5 @@
# coding=utf-8
from django.test import TestCase
# Create your tests here.

24
app/translation.py Normal file
파일 보기

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Copyright 2020 Jim Martens
#
# 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.
from modeltranslation import translator
from modeltranslation.translator import TranslationOptions
# example for translation
# @translator.register(Gender)
# class GenderTranslationOptions(TranslationOptions):
# fields = ('name',)

11
app/urls.py Normal file
파일 보기

@ -0,0 +1,11 @@
# coding=utf-8
from django.urls import path
from . import views
app_name = 'app'
urlpatterns = [
path('', views.index, name='index'),
path('legal-notice/', views.legal, name='legal'),
path('privacy/', views.privacy, name='privacy'),
]

122
app/views.py Normal file
파일 보기

@ -0,0 +1,122 @@
# coding=utf-8
from gettext import gettext as _
from django.contrib import messages
from django.contrib.auth import login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.views import LoginView
from django.db import transaction
from django.http import HttpResponse
from django.shortcuts import redirect
from django.shortcuts import render
from django.urls import reverse
from app.forms import ProfileForm
from app.forms import UserForm
def index(request) -> HttpResponse:
context = {
'site': {
'title': 'Django template'
}
}
if request.user.is_authenticated:
return render(request, template_name='app/home.html', context=context)
else:
return render(request, template_name='app/landing.html', context=context)
@transaction.atomic
def register(request) -> HttpResponse:
if request.user.is_authenticated:
return redirect('app:index')
user_form = UserCreationForm(data=request.POST or None)
profile_form = ProfileForm(data=request.POST or None, files=request.FILES or None)
context = {
'site': {
'title': 'Django template'
},
'user_form': user_form,
'profile_form': profile_form
}
if request.method == "POST" and user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'profile_pic' in request.FILES:
profile.profile_pic = request.FILES['profile_pic']
profile.save()
login(request, user=user)
messages.success(request, _('User was successfully registered'))
return redirect('edit_profile')
return render(request, template_name='registration/register.html', context=context)
def user_login(request) -> HttpResponse:
login_view = LoginView()
login_view.setup(request)
login_view.redirect_authenticated_user = True
login_view.extra_context = {
'active': 'login',
'title': _('Login'),
'site': {
'title': 'Django template'
},
}
return login_view.dispatch(request)
@login_required()
@transaction.atomic
def edit_profile(request) -> HttpResponse:
user_form = UserForm(data=request.POST or None, instance=request.user)
profile_form = ProfileForm(data=request.POST or None, files=request.FILES or None,
instance=request.user.profile)
user_form.fields['email'].help_text = _('The email address is required for password recovery.')
context = {
'active': 'edit_profile',
'title': _('Edit User Profile'),
'site': {
'title': 'Django template'
},
'user_form': user_form,
'profile_form': profile_form
}
if request.method == "POST" and user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile = profile_form.save(commit=False)
if 'profile_pic' in request.FILES:
profile.profile_pic = request.FILES['profile_pic']
profile.save()
messages.success(request, _('Profile was successfully updated'))
return render(request, template_name='registration/edit_profile.html', context=context)
@login_required()
def password_change_done(request) -> HttpResponse:
messages.success(request, _('Your password was successfully changed.'))
return redirect(reverse('edit_profile'))
def legal(request) -> HttpResponse:
context = {
'site': {
'title': 'Django template'
}
}
return render(request, template_name='app/legal.html', context=context)
def privacy(request) -> HttpResponse:
context = {
'site': {
'title': 'Django template'
}
}
return render(request, template_name='app/privacy.html', context=context)

22
manage.py Normal file
파일 보기

@ -0,0 +1,22 @@
#!/usr/bin/env python
# coding=utf-8
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'chaos_dating_project.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()

30
package.json Normal file
파일 보기

@ -0,0 +1,30 @@
{
"name": "django-template",
"version": "1.0.0",
"description": "Django template",
"main": "index.js",
"dependencies": {
"bootstrap": "^4.4.1",
"jquery": "^3.4.1",
"js-yaml": "3.13.1",
"popper.js": "^1.16.0"
},
"devDependencies": {
"autoprefixer": "^9.7.3",
"node-sass": "^4.12.0",
"sass-lint": "^1.13.1",
"eslint": "^4.18.2"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"compile:css": "node-sass -o static/css static/sass",
"compile:css:watch": "node-sass -o static/css static/sass --watch",
"lint:css": "sass-lint static/sass/** -v -q"
},
"repository": {
"type": "git",
"url": "https://git.2martens.de/2martens/django-template.git"
},
"author": "Jim Martens",
"license": "Apache-2.0"
}

1
project/__init__.py Normal file
파일 보기

@ -0,0 +1 @@
# coding=utf-8

17
project/asgi.py Normal file
파일 보기

@ -0,0 +1,17 @@
# coding=utf-8
"""
ASGI config for 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/3.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
application = get_asgi_application()

150
project/settings.py Normal file
파일 보기

@ -0,0 +1,150 @@
# coding=utf-8
"""
Django settings for project.
Generated by 'django-admin startproject' using Django 3.0.1.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
"""
import os
from django.utils.translation import gettext_lazy as _
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'w$#!92p2fg8lykxr*aqi-&0llq81xq+7pe^733p(qt*n!0g_ci')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ.get('DJANGO_DEBUG', 'True') != 'False'
# extend with allowed production hosts
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '[::1]']
# Application definition
INSTALLED_APPS = [
'app.apps.DjangoTemplateConfig', # TODO: change to your app config class
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'crispy_forms',
'modeltranslation',
]
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 = 'project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(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',
],
},
},
]
CRISPY_TEMPLATE_PACK = 'bootstrap4'
WSGI_APPLICATION = 'project.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/3.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',
},
]
AUTH_PROFILE_MODULE = 'app.Profile'
LOGIN_REDIRECT_URL = 'app:index'
LOGOUT_REDIRECT_URL = 'app:index'
LOGIN_URL = 'login'
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Europe/Berlin'
USE_I18N = True
USE_L10N = True
USE_TZ = True
LANGUAGES = [
('en-us', _('English')),
('de-de', _('German')),
]
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
# Security settings
SECURE_SSL_REDIRECT = not DEBUG
SESSION_COOKIE_SECURE = not DEBUG
CSRF_COOKIE_SECURE = not DEBUG
SECURE_REFERRER_POLICY = 'same-origin'

27
project/urls.py Normal file
파일 보기

@ -0,0 +1,27 @@
# coding=utf-8
"""project URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.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 include
from django.urls import path
from app import views
urlpatterns = [
path('admin/', admin.site.urls),
path('app/', include('app.urls')),
path('accounts/', include('app.account_urls')),
path('', views.index)
]

17
project/wsgi.py Normal file
파일 보기

@ -0,0 +1,17 @@
# coding=utf-8
"""
WSGI config for 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/3.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
application = get_wsgi_application()

3
requirements.txt Normal file
파일 보기

@ -0,0 +1,3 @@
django==3.0.3
django-crispy-forms==1.8.1
django-modeltranslation==0.14.2

10786
static/js/all.js Normal file

File diff suppressed because one or more lines are too long

파일 보기

@ -0,0 +1,11 @@
/*!
* Bootstrap v4.3.1 (https://getbootstrap.com/)
* Copyright 2011-2019 The Bootstrap Authors
* Copyright 2011-2019 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
// overwrite variables
$body-bg: #fdfdfd;
$enable-responsive-font-sizes: true;
@import "../../node_modules/bootstrap/scss/bootstrap";

파일 보기

@ -0,0 +1,342 @@
svg:not(:root).svg-inline--fa {
overflow: visible; }
.svg-inline--fa {
display: inline-block;
font-size: inherit;
height: 1em;
overflow: visible;
vertical-align: -.125em; }
.svg-inline--fa.fa-lg {
vertical-align: -.225em; }
.svg-inline--fa.fa-w-1 {
width: 0.0625em; }
.svg-inline--fa.fa-w-2 {
width: 0.125em; }
.svg-inline--fa.fa-w-3 {
width: 0.1875em; }
.svg-inline--fa.fa-w-4 {
width: 0.25em; }
.svg-inline--fa.fa-w-5 {
width: 0.3125em; }
.svg-inline--fa.fa-w-6 {
width: 0.375em; }
.svg-inline--fa.fa-w-7 {
width: 0.4375em; }
.svg-inline--fa.fa-w-8 {
width: 0.5em; }
.svg-inline--fa.fa-w-9 {
width: 0.5625em; }
.svg-inline--fa.fa-w-10 {
width: 0.625em; }
.svg-inline--fa.fa-w-11 {
width: 0.6875em; }
.svg-inline--fa.fa-w-12 {
width: 0.75em; }
.svg-inline--fa.fa-w-13 {
width: 0.8125em; }
.svg-inline--fa.fa-w-14 {
width: 0.875em; }
.svg-inline--fa.fa-w-15 {
width: 0.9375em; }
.svg-inline--fa.fa-w-16 {
width: 1em; }
.svg-inline--fa.fa-w-17 {
width: 1.0625em; }
.svg-inline--fa.fa-w-18 {
width: 1.125em; }
.svg-inline--fa.fa-w-19 {
width: 1.1875em; }
.svg-inline--fa.fa-w-20 {
width: 1.25em; }
.svg-inline--fa.fa-pull-left {
margin-right: .3em;
width: auto; }
.svg-inline--fa.fa-pull-right {
margin-left: .3em;
width: auto; }
.svg-inline--fa.fa-border {
height: 1.5em; }
.svg-inline--fa.fa-li {
width: 2em; }
.svg-inline--fa.fa-fw {
width: 1.25em; }
.fa-layers svg.svg-inline--fa {
bottom: 0;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0; }
.fa-layers {
display: inline-block;
height: 1em;
position: relative;
text-align: center;
vertical-align: -.125em;
width: 1em; }
.fa-layers svg.svg-inline--fa {
-webkit-transform-origin: center center;
transform-origin: center center; }
.fa-layers-text, .fa-layers-counter {
display: inline-block;
position: absolute;
text-align: center; }
.fa-layers-text {
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
-webkit-transform-origin: center center;
transform-origin: center center; }
.fa-layers-counter {
background-color: #ff253a;
border-radius: 1em;
-webkit-box-sizing: border-box;
box-sizing: border-box;
color: #fff;
height: 1.5em;
line-height: 1;
max-width: 5em;
min-width: 1.5em;
overflow: hidden;
padding: .25em;
right: 0;
text-overflow: ellipsis;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top right;
transform-origin: top right; }
.fa-layers-bottom-right {
bottom: 0;
right: 0;
top: auto;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: bottom right;
transform-origin: bottom right; }
.fa-layers-bottom-left {
bottom: 0;
left: 0;
right: auto;
top: auto;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: bottom left;
transform-origin: bottom left; }
.fa-layers-top-right {
right: 0;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top right;
transform-origin: top right; }
.fa-layers-top-left {
left: 0;
right: auto;
top: 0;
-webkit-transform: scale(0.25);
transform: scale(0.25);
-webkit-transform-origin: top left;
transform-origin: top left; }
.fa-lg {
font-size: 1.33333em;
line-height: 0.75em;
vertical-align: -.0667em; }
.fa-xs {
font-size: .75em; }
.fa-sm {
font-size: .875em; }
.fa-1x {
font-size: 1em; }
.fa-2x {
font-size: 2em; }
.fa-3x {
font-size: 3em; }
.fa-4x {
font-size: 4em; }
.fa-5x {
font-size: 5em; }
.fa-6x {
font-size: 6em; }
.fa-7x {
font-size: 7em; }
.fa-8x {
font-size: 8em; }
.fa-9x {
font-size: 9em; }
.fa-10x {
font-size: 10em; }
.fa-fw {
text-align: center;
width: 1.25em; }
.fa-ul {
list-style-type: none;
margin-left: 2.5em;
padding-left: 0; }
.fa-ul > li {
position: relative; }
.fa-li {
left: -2em;
position: absolute;
text-align: center;
width: 2em;
line-height: inherit; }
.fa-border {
border: solid 0.08em #eee;
border-radius: .1em;
padding: .2em .25em .15em; }
.fa-pull-left {
float: left; }
.fa-pull-right {
float: right; }
.fa.fa-pull-left,
.fas.fa-pull-left,
.far.fa-pull-left,
.fal.fa-pull-left,
.fab.fa-pull-left {
margin-right: .3em; }
.fa.fa-pull-right,
.fas.fa-pull-right,
.far.fa-pull-right,
.fal.fa-pull-right,
.fab.fa-pull-right {
margin-left: .3em; }
.fa-spin {
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear; }
.fa-pulse {
-webkit-animation: fa-spin 1s infinite steps(8);
animation: fa-spin 1s infinite steps(8); }
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
@keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
.fa-rotate-90 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
transform: rotate(90deg); }
.fa-rotate-180 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
transform: rotate(180deg); }
.fa-rotate-270 {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
transform: rotate(270deg); }
.fa-flip-horizontal {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
transform: scale(-1, 1); }
.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
transform: scale(1, -1); }
.fa-flip-both, .fa-flip-horizontal.fa-flip-vertical {
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(-1, -1);
transform: scale(-1, -1); }
:root .fa-rotate-90,
:root .fa-rotate-180,
:root .fa-rotate-270,
:root .fa-flip-horizontal,
:root .fa-flip-vertical,
:root .fa-flip-both {
-webkit-filter: none;
filter: none; }
.fa-stack {
display: inline-block;
height: 2em;
position: relative;
width: 2.5em; }
.fa-stack-1x,
.fa-stack-2x {
bottom: 0;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0; }
.svg-inline--fa.fa-stack-1x {
height: 1em;
width: 1.25em; }
.svg-inline--fa.fa-stack-2x {
height: 2em;
width: 2.5em; }
.fa-inverse {
color: #fff; }
.sr-only {
border: 0;
clip: rect(0, 0, 0, 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px; }
.sr-only-focusable:active, .sr-only-focusable:focus {
clip: auto;
height: auto;
margin: 0;
overflow: visible;
position: static;
width: auto; }

59
static/sass/main.scss Normal file
파일 보기

@ -0,0 +1,59 @@
@charset "UTF-8";
@import "bootstrap-custom";
@import "fa-svg-with-js";
.site-header > .navbar {
//border-top: 5px solid $dark;
border-bottom: 2px solid $light;
}
// nav tabs
.nav-tabs {
margin-bottom: 1rem;
}
.euro {
text-align: right;
}
// back-to-top button
#back-to-top {
cursor: pointer;
position: -webkit-sticky;
position: sticky;
bottom: 40px;
display:none;
}
$navbarHeight: 58px;
#main {
margin-top: $navbarHeight + 15px;
margin-bottom: $navbarHeight + 15px;
}
#main.submenu {
margin-top: 2*$navbarHeight + 15px;
}
#countdown:before {
display: inline;
}
.site-header > .navbar {
background-color: $body-bg;
}
.site-footer {
border-top: 2px solid $light;
background-color: $body-bg;
}
.anchor {
position: relative;
display: block;
top: -$navbarHeight;
visibility: hidden;
}

0
templates/.gitkeep Normal file
파일 보기

68
templates/base.html Normal file
파일 보기

@ -0,0 +1,68 @@
{% load static %}
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{% block meta %}{% endblock %}
<!-- FontAwesome -->
<script defer src="{% static "js/all.js" %}"></script>
<link type="text/css" href="{% static 'css/main.css' %}" rel="stylesheet" media="screen">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<header class="site-header">
{% block header %}{% endblock %}
</header>
<div id="main">
<div class="container">
<div class="row">
<div class="col">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
</div>
</div>
<div class="row">
<main class="col">
{% block content %}{% endblock %}
</main>
</div>
<button id="back-to-top"
class="btn btn-primary btn-lg"
title="Click to return to the start of the page" data-toggle="tooltip" data-placement="left">
<span class="fas fa-chevron-up"></span>
</button>
</div>
</div>
<footer class="site-footer small text-muted py-3">
<div class="container">
<div class="row">
{% block footer %}{% endblock %}
</div>
</div>
</footer>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
crossorigin="anonymous"></script>
{% block javascripts %}{% endblock %}
</body>
</html>

2395
yarn.lock Normal file

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다. Load Diff