Merge branch 'master' into task/3554/remove_beta_url
This commit is contained in:
commit
488d259ec5
17 changed files with 359 additions and 20 deletions
|
@ -4,7 +4,7 @@ python:
|
||||||
# - "3.6"
|
# - "3.6"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- DJANGO_SECRET_KEY=0 OPENNEBULA_USERNAME='test' OPENNEBULA_PASSWORD='test' OPENNEBULA_PROTOCOL='http' OPENNEBULA_DOMAIN='test_domain' OPENNEBULA_PORT='2633' OPENNEBULA_ENDPOINT='/RPC2' DCL_TEXT='Data Center Light' CELERY_MAX_RETRIES=0
|
- DJANGO_SECRET_KEY=0 OPENNEBULA_USERNAME='test' OPENNEBULA_PASSWORD='test' OPENNEBULA_PROTOCOL='http' OPENNEBULA_DOMAIN='test_domain' OPENNEBULA_PORT='2633' OPENNEBULA_ENDPOINT='/RPC2' DCL_TEXT='Data Center Light' CELERY_MAX_RETRIES=0 UNGLEICH_SITE_CONFIGS='{"localhost":{"MULTISITE_CMS_URL":"dynamicweb.urls"}}'
|
||||||
# install dependencies
|
# install dependencies
|
||||||
install: "pip install -r requirements.txt"
|
install: "pip install -r requirements.txt"
|
||||||
script:
|
script:
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
Next:
|
1.4: 2018-02-22
|
||||||
|
* #4104: [cms, nuglarus] Multisite and access control of cms pages per user
|
||||||
|
1.3.3: 2018-02-21
|
||||||
|
* Add ALLOWED_HOST nüglarus.ch IDN
|
||||||
|
* #4105: [cms] Add cms footer plugin
|
||||||
* #4049: [blog] Replace header background image
|
* #4049: [blog] Replace header background image
|
||||||
* #3670: [hosting] Shorten ssh key name
|
* #3670: [hosting] Shorten ssh key name
|
||||||
* #4046: [hosting] Add sdd_size, hdd_size to VirtualMachineSerializer (No visual change)
|
* #4046: [hosting] Add sdd_size, hdd_size to VirtualMachineSerializer (No visual change)
|
||||||
|
|
|
@ -5,6 +5,7 @@ Copyright 2015 ungleich.
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -54,7 +55,8 @@ PROJECT_DIR = os.path.abspath(
|
||||||
# load .env file
|
# load .env file
|
||||||
dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR))
|
dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR))
|
||||||
|
|
||||||
SITE_ID = 1
|
from multisite import SiteID
|
||||||
|
SITE_ID = SiteID(default=1)
|
||||||
|
|
||||||
APP_ROOT_ENDPOINT = "/"
|
APP_ROOT_ENDPOINT = "/"
|
||||||
APPEND_SLASH = True
|
APPEND_SLASH = True
|
||||||
|
@ -76,6 +78,7 @@ SECRET_KEY = env('DJANGO_SECRET_KEY')
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
# 1st migrate
|
# 1st migrate
|
||||||
'membership',
|
'membership',
|
||||||
|
'djangocms_admin_style',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
@ -83,6 +86,8 @@ INSTALLED_APPS = (
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'django.contrib.sites',
|
'django.contrib.sites',
|
||||||
|
'multisite',
|
||||||
|
'djangocms_multisite',
|
||||||
'easy_thumbnails',
|
'easy_thumbnails',
|
||||||
'utils',
|
'utils',
|
||||||
'stored_messages',
|
'stored_messages',
|
||||||
|
@ -124,7 +129,6 @@ INSTALLED_APPS = (
|
||||||
# 'djangocms_teaser',
|
# 'djangocms_teaser',
|
||||||
'djangocms_page_meta',
|
'djangocms_page_meta',
|
||||||
'djangocms_text_ckeditor',
|
'djangocms_text_ckeditor',
|
||||||
'djangocms_admin_style',
|
|
||||||
'cmsplugin_filer_file',
|
'cmsplugin_filer_file',
|
||||||
'cmsplugin_filer_folder',
|
'cmsplugin_filer_folder',
|
||||||
'cmsplugin_filer_link',
|
'cmsplugin_filer_link',
|
||||||
|
@ -163,6 +167,8 @@ MIDDLEWARE_CLASSES = (
|
||||||
'cms.middleware.page.CurrentPageMiddleware',
|
'cms.middleware.page.CurrentPageMiddleware',
|
||||||
'cms.middleware.toolbar.ToolbarMiddleware',
|
'cms.middleware.toolbar.ToolbarMiddleware',
|
||||||
'cms.middleware.language.LanguageCookieMiddleware',
|
'cms.middleware.language.LanguageCookieMiddleware',
|
||||||
|
'multisite.middleware.DynamicSiteMiddleware',
|
||||||
|
'djangocms_multisite.middleware.CMSMultiSiteMiddleware',
|
||||||
)
|
)
|
||||||
|
|
||||||
CSRF_FAILURE_VIEW = 'hosting.views.forbidden_view'
|
CSRF_FAILURE_VIEW = 'hosting.views.forbidden_view'
|
||||||
|
@ -328,6 +334,8 @@ CMS_PLACEHOLDER_CONF = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CMS_PERMISSION=True
|
||||||
|
|
||||||
CACHES = {
|
CACHES = {
|
||||||
'default': {
|
'default': {
|
||||||
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
|
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
|
||||||
|
@ -507,6 +515,36 @@ STRIPE_API_PRIVATE_KEY_TEST = env('STRIPE_API_PRIVATE_KEY_TEST')
|
||||||
ANONYMOUS_USER_NAME = 'anonymous@ungleich.ch'
|
ANONYMOUS_USER_NAME = 'anonymous@ungleich.ch'
|
||||||
GUARDIAN_GET_INIT_ANONYMOUS_USER = 'membership.models.get_anonymous_user_instance'
|
GUARDIAN_GET_INIT_ANONYMOUS_USER = 'membership.models.get_anonymous_user_instance'
|
||||||
|
|
||||||
|
UNGLEICH_SITE_CONFIGS = env('UNGLEICH_SITE_CONFIGS')
|
||||||
|
|
||||||
|
MULTISITE_CMS_URLS = {}
|
||||||
|
if UNGLEICH_SITE_CONFIGS == "":
|
||||||
|
raise Exception("Please define UNGLEICH_SITE_CONFIGS in your .env")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
configs_dict=json.loads(UNGLEICH_SITE_CONFIGS)
|
||||||
|
except ValueError as verr:
|
||||||
|
raise Exception("UNGLEICH_SITE_CONFIGS is not a valid JSON: {}".format(
|
||||||
|
str(verr)
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
MULTISITE_CMS_URLS = {
|
||||||
|
k:v['MULTISITE_CMS_URL'] for (k,v) in configs_dict.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
MULTISITE_CMS_ALIASES = {
|
||||||
|
}
|
||||||
|
MULTISITE_CMS_FALLBACK = env('MULTISITE_CMS_FALLBACK')
|
||||||
|
if MULTISITE_CMS_FALLBACK == '':
|
||||||
|
MULTISITE_CMS_FALLBACK = 'datacenterlight.ch'
|
||||||
|
MULTISITE_FALLBACK = 'django.views.generic.base.RedirectView'
|
||||||
|
MULTISITE_FALLBACK_KWARGS = {
|
||||||
|
'url': 'https://{}/'.format(MULTISITE_CMS_FALLBACK), 'permanent': False
|
||||||
|
}
|
||||||
|
|
||||||
|
FILER_ENABLE_PERMISSIONS = True
|
||||||
|
|
||||||
|
|
||||||
#############################################
|
#############################################
|
||||||
# configurations for opennebula-integration #
|
# configurations for opennebula-integration #
|
||||||
#############################################
|
#############################################
|
||||||
|
|
|
@ -19,5 +19,6 @@ MIDDLEWARE_CLASSES += ("debug_toolbar.middleware.DebugToolbarMiddleware",)
|
||||||
|
|
||||||
INSTALLED_APPS += (
|
INSTALLED_APPS += (
|
||||||
'django_extensions',
|
'django_extensions',
|
||||||
'debug_toolbar'
|
# debug_toolbar seems to conflict with multisite (and djangocms_multisite)
|
||||||
|
# 'debug_toolbar'
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,5 +31,6 @@ ALLOWED_HOSTS = [
|
||||||
".ipv6onlyhosting.com",
|
".ipv6onlyhosting.com",
|
||||||
".ipv6onlyhosting.net",
|
".ipv6onlyhosting.net",
|
||||||
".digitalglarus.ch",
|
".digitalglarus.ch",
|
||||||
".hack4glarus.ch"
|
".hack4glarus.ch",
|
||||||
|
".xn--nglarus-n2a.ch"
|
||||||
]
|
]
|
||||||
|
|
17
dynamicweb/urls_multi.py
Normal file
17
dynamicweb/urls_multi.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.conf.urls import include, url
|
||||||
|
from django.conf.urls.i18n import i18n_patterns
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.views import static as static_view
|
||||||
|
|
||||||
|
urlpatterns = i18n_patterns(
|
||||||
|
url(r'^admin/', include(admin.site.urls)),
|
||||||
|
url(r'^cms/', include('cms.urls')),
|
||||||
|
)
|
||||||
|
|
||||||
|
urlpatterns += [
|
||||||
|
url(r'^media/(?P<path>.*)$',
|
||||||
|
static_view.serve, {
|
||||||
|
'document_root': settings.MEDIA_ROOT,
|
||||||
|
}),
|
||||||
|
]
|
|
@ -1,20 +1,103 @@
|
||||||
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
|
from django.contrib.auth.forms import ReadOnlyPasswordHashField
|
||||||
|
|
||||||
from .models import CustomUser, StripeCustomer
|
from .models import CustomUser, StripeCustomer
|
||||||
from django.contrib.auth.hashers import make_password
|
|
||||||
|
|
||||||
|
|
||||||
class CustomUserAdmin(admin.ModelAdmin):
|
# Refer https://docs.djangoproject.com/en/2.0/topics/auth/customizing/
|
||||||
fields = ('password', 'user_permissions', 'email', 'is_admin')
|
# for understanding custom auth user model
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
|
||||||
password = form.cleaned_data.get('password')
|
|
||||||
|
|
||||||
if not change:
|
class UserCreationForm(forms.ModelForm):
|
||||||
obj.validation_slug = make_password(None)
|
"""A form for creating new users. Includes all the required
|
||||||
|
fields, plus a repeated password."""
|
||||||
|
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
|
||||||
|
password2 = forms.CharField(label='Password confirmation',
|
||||||
|
widget=forms.PasswordInput)
|
||||||
|
|
||||||
obj.set_password(password)
|
class Meta:
|
||||||
obj.save()
|
model = CustomUser
|
||||||
return obj
|
fields = ('email', 'user_permissions', 'email', 'is_admin')
|
||||||
|
|
||||||
|
def clean_password2(self):
|
||||||
|
# Check that the two password entries match
|
||||||
|
password1 = self.cleaned_data.get("password1")
|
||||||
|
password2 = self.cleaned_data.get("password2")
|
||||||
|
if password1 and password2 and password1 != password2:
|
||||||
|
raise forms.ValidationError("Passwords don't match")
|
||||||
|
return password2
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
# Save the provided password in hashed format
|
||||||
|
user = super().save(commit=False)
|
||||||
|
user.set_password(self.cleaned_data["password1"])
|
||||||
|
if commit:
|
||||||
|
user.save()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
class UserChangeForm(forms.ModelForm):
|
||||||
|
"""A form for updating users. Includes all the fields on
|
||||||
|
the user, but replaces the password field with admin's
|
||||||
|
password hash display field.
|
||||||
|
"""
|
||||||
|
password = ReadOnlyPasswordHashField(
|
||||||
|
label="Password",
|
||||||
|
help_text=(
|
||||||
|
"Raw passwords are not stored, so there is no way to see "
|
||||||
|
"this user's password, but you can change the password "
|
||||||
|
"using <a href=\"../password/\">this form</a>.")
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CustomUser
|
||||||
|
fields = ('email', 'password', 'is_admin')
|
||||||
|
|
||||||
|
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["password"]
|
||||||
|
|
||||||
|
|
||||||
|
class CustomUserAdmin(BaseUserAdmin):
|
||||||
|
# The forms to add and change user instances
|
||||||
|
form = UserChangeForm
|
||||||
|
add_form = UserCreationForm
|
||||||
|
|
||||||
|
# The fields to be used in displaying the User model.
|
||||||
|
# These override the definitions on the base UserAdmin
|
||||||
|
# that reference specific fields on auth.User.
|
||||||
|
list_display = (
|
||||||
|
'email', 'is_admin', 'is_superuser'
|
||||||
|
)
|
||||||
|
list_filter = ()
|
||||||
|
fieldsets = (
|
||||||
|
(None, {'fields': ('email',)}),
|
||||||
|
('Change Password',
|
||||||
|
{'fields': ('password',),
|
||||||
|
'description': "Raw passwords are not stored, so there is no way to "
|
||||||
|
"see this user's password, but you can change the "
|
||||||
|
"password using <a href=\"../password/\">this "
|
||||||
|
"form</a>."
|
||||||
|
}
|
||||||
|
),
|
||||||
|
('Permissions', {'fields': ('is_admin', 'user_permissions',
|
||||||
|
'groups')}),
|
||||||
|
)
|
||||||
|
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
|
||||||
|
# overrides get_fieldsets to use this attribute when creating a user.
|
||||||
|
add_fieldsets = (
|
||||||
|
(None, {
|
||||||
|
'classes': ('wide',),
|
||||||
|
'fields': ('email', 'password1', 'password2')}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
search_fields = ('email',)
|
||||||
|
ordering = ('email',)
|
||||||
|
filter_horizontal = ()
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(CustomUser, CustomUserAdmin)
|
admin.site.register(CustomUser, CustomUserAdmin)
|
||||||
|
|
21
membership/migrations/0007_auto_20180213_0128.py
Normal file
21
membership/migrations/0007_auto_20180213_0128.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2018-02-13 01:28
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import membership.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('membership', '0006_auto_20160526_0445'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='customuser',
|
||||||
|
name='validation_slug',
|
||||||
|
field=models.CharField(db_index=True, default=membership.models.get_validation_slug, max_length=50, unique=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -59,6 +59,10 @@ class MyUserManager(BaseUserManager):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def get_validation_slug():
|
||||||
|
return make_password(None)
|
||||||
|
|
||||||
|
|
||||||
class CustomUser(AbstractBaseUser, PermissionsMixin):
|
class CustomUser(AbstractBaseUser, PermissionsMixin):
|
||||||
VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated'))
|
VALIDATED_CHOICES = ((0, 'Not validated'), (1, 'Validated'))
|
||||||
site = models.ForeignKey(Site, default=1)
|
site = models.ForeignKey(Site, default=1)
|
||||||
|
@ -66,8 +70,12 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
|
||||||
email = models.EmailField(unique=True)
|
email = models.EmailField(unique=True)
|
||||||
|
|
||||||
validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0)
|
validated = models.IntegerField(choices=VALIDATED_CHOICES, default=0)
|
||||||
validation_slug = models.CharField(db_index=True, unique=True,
|
# By default, we initialize the validation_slug with appropriate value
|
||||||
max_length=50)
|
# This is required for User(page) admin
|
||||||
|
validation_slug = models.CharField(
|
||||||
|
db_index=True, unique=True, max_length=50,
|
||||||
|
default=get_validation_slug
|
||||||
|
)
|
||||||
is_admin = models.BooleanField(
|
is_admin = models.BooleanField(
|
||||||
_('staff status'),
|
_('staff status'),
|
||||||
default=False,
|
default=False,
|
||||||
|
@ -171,6 +179,10 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
|
||||||
# Simplest possible answer: All admins are staff
|
# Simplest possible answer: All admins are staff
|
||||||
return self.is_admin
|
return self.is_admin
|
||||||
|
|
||||||
|
@is_staff.setter
|
||||||
|
def is_staff(self, value):
|
||||||
|
self._is_staff = value
|
||||||
|
|
||||||
|
|
||||||
class StripeCustomer(models.Model):
|
class StripeCustomer(models.Model):
|
||||||
user = models.OneToOneField(CustomUser)
|
user = models.OneToOneField(CustomUser)
|
||||||
|
|
|
@ -104,7 +104,7 @@ class VirtualMachineSerializer(serializers.Serializer):
|
||||||
disk_size += int(disk.size)
|
disk_size += int(disk.size)
|
||||||
return disk_size / 1024
|
return disk_size / 1024
|
||||||
|
|
||||||
def get_sdd_size(self, obj):
|
def get_ssd_size(self, obj):
|
||||||
template = obj.template
|
template = obj.template
|
||||||
disk_size = 0
|
disk_size = 0
|
||||||
for disk in template.disks:
|
for disk in template.disks:
|
||||||
|
|
|
@ -34,6 +34,7 @@ django-meta==1.2
|
||||||
django-meta-mixin==0.3.0
|
django-meta-mixin==0.3.0
|
||||||
django-model-utils==2.5
|
django-model-utils==2.5
|
||||||
django-mptt==0.8.4
|
django-mptt==0.8.4
|
||||||
|
django-multisite==1.4.1
|
||||||
django-parler==1.6.3
|
django-parler==1.6.3
|
||||||
django-phonenumber-field==1.1.0
|
django-phonenumber-field==1.1.0
|
||||||
django-polymorphic==0.9.2
|
django-polymorphic==0.9.2
|
||||||
|
@ -97,3 +98,4 @@ billiard==3.5.0.3
|
||||||
amqp==2.2.1
|
amqp==2.2.1
|
||||||
vine==1.1.4
|
vine==1.1.4
|
||||||
cdist==4.7.0
|
cdist==4.7.0
|
||||||
|
git+https://github.com/ungleich/djangocms-multisite.git#egg=djangocms_multisite
|
||||||
|
|
|
@ -8,6 +8,7 @@ from .models import (
|
||||||
UngleichCustomerItem, UngleichHTMLOnly,
|
UngleichCustomerItem, UngleichHTMLOnly,
|
||||||
UngleichHeaderWithBackgroundImageSlider,
|
UngleichHeaderWithBackgroundImageSlider,
|
||||||
UngleichHeaderWithBackgroundVideoSliderItem,
|
UngleichHeaderWithBackgroundVideoSliderItem,
|
||||||
|
UngleichFooter
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -295,3 +296,18 @@ class UngleichHTMLPlugin(CMSPluginBase):
|
||||||
)
|
)
|
||||||
context['instance'] = instance
|
context['instance'] = instance
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@plugin_pool.register_plugin
|
||||||
|
class UngleichFooterPlugin(CMSPluginBase):
|
||||||
|
name = "ungleich Footer Plugin"
|
||||||
|
model = UngleichFooter
|
||||||
|
render_template = "ungleich_page/ungleich/_footer.html"
|
||||||
|
cache = False
|
||||||
|
|
||||||
|
def render(self, context, instance, placeholder):
|
||||||
|
context = super(UngleichFooterPlugin, self).render(
|
||||||
|
context, instance, placeholder
|
||||||
|
)
|
||||||
|
context['instance'] = instance
|
||||||
|
return context
|
||||||
|
|
44
ungleich_page/migrations/0018_ungleichfooter.py
Normal file
44
ungleich_page/migrations/0018_ungleichfooter.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2018-02-08 15:49
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('ungleich_page', '0017_auto_20171219_1856'),
|
||||||
|
('cms', '0014_auto_20160404_1908'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UngleichFooter',
|
||||||
|
fields=[
|
||||||
|
('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
parent_link=True, primary_key=True, serialize=False, to='cms.CMSPlugin')),
|
||||||
|
('copyright_label', models.CharField(blank=True, default='',
|
||||||
|
help_text='Name of the company alongside the copyright year', max_length=100)),
|
||||||
|
('link_text', models.CharField(
|
||||||
|
blank=True, help_text='Text for the link on the right part of footer', max_length=100, null=True)),
|
||||||
|
('link_url', models.URLField(blank=True,
|
||||||
|
help_text='Url to the link in footer', null=True)),
|
||||||
|
('twitter_url', models.URLField(
|
||||||
|
blank=True, help_text='If empty, twitter btn will not be visible', null=True)),
|
||||||
|
('linkedin_url', models.URLField(
|
||||||
|
blank=True, help_text='If empty, linkedin btn will not be visible', null=True)),
|
||||||
|
('github_url', models.URLField(
|
||||||
|
blank=True, help_text='If empty, github btn will not be visible', null=True)),
|
||||||
|
('facebook_url', models.URLField(
|
||||||
|
blank=True, help_text='If empty, facebook btn will not be visible', null=True)),
|
||||||
|
('youtube_url', models.URLField(
|
||||||
|
blank=True, help_text='If empty, youtube btn will not be visible', null=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
bases=('cms.cmsplugin',),
|
||||||
|
),
|
||||||
|
]
|
16
ungleich_page/migrations/0019_merge.py
Normal file
16
ungleich_page/migrations/0019_merge.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.4 on 2018-02-08 20:10
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('ungleich_page', '0018_ungleichfooter'),
|
||||||
|
('ungleich_page', '0018_auto_20180105_1826'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
]
|
|
@ -169,3 +169,38 @@ class UngleichHTMLOnly(CMSPlugin):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class UngleichFooter(CMSPlugin):
|
||||||
|
copyright_label = models.CharField(
|
||||||
|
max_length=100, default='', blank=True,
|
||||||
|
help_text='Name of the company alongside the copyright year'
|
||||||
|
)
|
||||||
|
link_text = models.CharField(
|
||||||
|
max_length=100, blank=True, null=True,
|
||||||
|
help_text='Text for the link on the right part of footer'
|
||||||
|
)
|
||||||
|
link_url = models.URLField(
|
||||||
|
blank=True, null=True,
|
||||||
|
help_text='Url to the link in footer'
|
||||||
|
)
|
||||||
|
twitter_url = models.URLField(
|
||||||
|
blank=True, null=True,
|
||||||
|
help_text='If empty, twitter btn will not be visible'
|
||||||
|
)
|
||||||
|
linkedin_url = models.URLField(
|
||||||
|
blank=True, null=True,
|
||||||
|
help_text='If empty, linkedin btn will not be visible'
|
||||||
|
)
|
||||||
|
github_url = models.URLField(
|
||||||
|
blank=True, null=True,
|
||||||
|
help_text='If empty, github btn will not be visible'
|
||||||
|
)
|
||||||
|
facebook_url = models.URLField(
|
||||||
|
blank=True, null=True,
|
||||||
|
help_text='If empty, facebook btn will not be visible'
|
||||||
|
)
|
||||||
|
youtube_url = models.URLField(
|
||||||
|
blank=True, null=True,
|
||||||
|
help_text='If empty, youtube btn will not be visible'
|
||||||
|
)
|
||||||
|
|
47
ungleich_page/templates/ungleich_page/ungleich/_footer.html
Normal file
47
ungleich_page/templates/ungleich_page/ungleich/_footer.html
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<footer>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<span class="copyright">Copyright © {{instance.copyright_label}} {% now "Y" %}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<ul class="list-inline social-buttons">
|
||||||
|
{% if instance.twitter_url %}
|
||||||
|
<li>
|
||||||
|
<a href="{{instance.twitter_url}}"><i class="fa fa-twitter"></i></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if instance.github_url %}
|
||||||
|
<li>
|
||||||
|
<a href="{{instance.github_url}}"><i class="fa fa-github"></i></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if instance.linkedin_url %}
|
||||||
|
<li>
|
||||||
|
<a href="{{instance.linkedin_url}}"><i class="fa fa-linkedin"></i></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if instance.facebook_url %}
|
||||||
|
<li>
|
||||||
|
<a href="{{instance.facebook_url}}"><i class="fa fa-facebook"></i></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if instance.youtube_url %}
|
||||||
|
<li>
|
||||||
|
<a href="{{instance.youtube_url}}"><i class="fa fa-youtube-play"></i></a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
{% if instance.link_text %}
|
||||||
|
<ul class="list-inline quicklinks">
|
||||||
|
<li>
|
||||||
|
<a href="{{instance.link_url}}">{{instance.link_text}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
|
@ -41,7 +41,9 @@
|
||||||
{% placeholder 'Ungleich Page Contents' %}
|
{% placeholder 'Ungleich Page Contents' %}
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
|
{% placeholder 'Footer' or %}
|
||||||
{% include "ungleich_page/includes/_footer.html" %}
|
{% include "ungleich_page/includes/_footer.html" %}
|
||||||
|
{% endplaceholder %}
|
||||||
|
|
||||||
<!-- jQuery -->
|
<!-- jQuery -->
|
||||||
<script src="{% static 'ungleich_page/js/jquery.js' %}" type="text/javascript"></script>
|
<script src="{% static 'ungleich_page/js/jquery.js' %}" type="text/javascript"></script>
|
||||||
|
|
Loading…
Reference in a new issue