added gallery and portal informations
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from parler.admin import TranslatableAdmin
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
|
||||
from portal.models import Profile, Information, DownloadSection, DownloadTag, DownloadFile
|
||||
from portal.models import Profile, Announcement, DownloadSection, DownloadTag, DownloadFile, Information, \
|
||||
InformationSection
|
||||
|
||||
|
||||
class ProfileInline(admin.StackedInline):
|
||||
@@ -25,8 +25,8 @@ admin.site.unregister(User)
|
||||
admin.site.register(User, UserAdmin)
|
||||
|
||||
|
||||
@admin.register(Information)
|
||||
class InformationAdmin(TranslatableAdmin):
|
||||
@admin.register(Announcement)
|
||||
class AnnouncementAdmin(TranslatableAdmin):
|
||||
list_display = ('title', 'published')
|
||||
list_filter = ('groups',)
|
||||
filter_horizontal = ('groups',)
|
||||
@@ -38,6 +38,18 @@ class InformationAdmin(TranslatableAdmin):
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Information)
|
||||
class InformationAdmin(TranslatableAdmin):
|
||||
list_display = ('title', 'published')
|
||||
list_filter = ('groups',)
|
||||
filter_horizontal = ('groups',)
|
||||
|
||||
|
||||
@admin.register(InformationSection)
|
||||
class InformationSectionAdmin(TranslatableAdmin):
|
||||
pass
|
||||
|
||||
|
||||
@admin.register(DownloadSection)
|
||||
class DownloadSectionAdmin(TranslatableAdmin):
|
||||
list_display = ('title', 'ordering')
|
||||
|
||||
130
src/portal/migrations/0002_auto_20180425_1334.py
Normal file
130
src/portal/migrations/0002_auto_20180425_1334.py
Normal file
@@ -0,0 +1,130 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.8post1 on 2018-04-25 13:34
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import cms.models.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import image_cropping.fields
|
||||
import parler.models
|
||||
import project.utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cms', '0018_pagenode'),
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
migrations.swappable_dependency(settings.FILER_IMAGE_MODEL),
|
||||
('portal', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Announcement',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('cropping', image_cropping.fields.ImageRatioField('image', '1200x800', adapt_rotation=False, allow_fullsize=False, free_crop=True, help_text=None, hide_image_field=False, size_warning=False, verbose_name='cropping')),
|
||||
('published', models.BooleanField(default=False, verbose_name='Veröffentlicht')),
|
||||
('updated', models.DateTimeField(auto_now=True, verbose_name='Aktualisiert')),
|
||||
('groups', models.ManyToManyField(related_name='announcements', to='auth.Group', verbose_name='Mitglieder Gruppe')),
|
||||
('image', project.utils.CroppableFilerImageField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.FILER_IMAGE_MODEL, verbose_name='Bild')),
|
||||
('informed_users', models.ManyToManyField(blank=True, null=True, to=settings.AUTH_USER_MODEL, verbose_name='Als gelesen markiert von:')),
|
||||
('placeholder', cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, slotname='content', to='cms.Placeholder')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-updated'],
|
||||
'verbose_name': 'Neuigkeit',
|
||||
'verbose_name_plural': 'Neuigkeiten',
|
||||
},
|
||||
bases=(parler.models.TranslatableModelMixin, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AnnouncementTranslation',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('language_code', models.CharField(db_index=True, max_length=15, verbose_name='Language')),
|
||||
('title', models.CharField(max_length=100, verbose_name='Title')),
|
||||
('master', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='portal.Announcement')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'portal_announcement_translation',
|
||||
'verbose_name': 'Neuigkeit Translation',
|
||||
'db_tablespace': '',
|
||||
'default_permissions': (),
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InformationSection',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('ordering', models.IntegerField(default=50, verbose_name='Sortierung')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['ordering'],
|
||||
'verbose_name': 'Information Section',
|
||||
'verbose_name_plural': 'Information Sections',
|
||||
},
|
||||
bases=(parler.models.TranslatableModelMixin, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='InformationSectionTranslation',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('language_code', models.CharField(db_index=True, max_length=15, verbose_name='Language')),
|
||||
('title', models.CharField(max_length=100, verbose_name='Title')),
|
||||
('master', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='portal.InformationSection')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'portal_informationsection_translation',
|
||||
'verbose_name': 'Information Section Translation',
|
||||
'db_tablespace': '',
|
||||
'default_permissions': (),
|
||||
'managed': True,
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='information',
|
||||
options={'verbose_name': 'Information', 'verbose_name_plural': 'Informationen'},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='information',
|
||||
name='informed_users',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='information',
|
||||
name='updated',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='informationtranslation',
|
||||
name='description',
|
||||
field=models.TextField(default='', verbose_name='Beschreibung'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='informationtranslation',
|
||||
name='tag_list',
|
||||
field=models.TextField(blank=True, help_text='Mit Komma getrennt', null=True, verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='information',
|
||||
name='groups',
|
||||
field=models.ManyToManyField(related_name='informations', to='auth.Group', verbose_name='Mitglieder Gruppe'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='information',
|
||||
name='section',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='informations', to='portal.InformationSection', verbose_name='Information Section'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='informationsectiontranslation',
|
||||
unique_together=set([('language_code', 'master')]),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='announcementtranslation',
|
||||
unique_together=set([('language_code', 'master')]),
|
||||
),
|
||||
]
|
||||
@@ -35,8 +35,8 @@ class Profile(models.Model):
|
||||
return '{} {}'.format(self.first_name, self.last_name)
|
||||
|
||||
|
||||
class Information(TranslatableModel):
|
||||
groups = models.ManyToManyField(Group, verbose_name='Mitglieder Gruppe', related_name='tasks')
|
||||
class Announcement(TranslatableModel):
|
||||
groups = models.ManyToManyField(Group, verbose_name='Mitglieder Gruppe', related_name='announcements')
|
||||
image = CroppableFilerImageField(verbose_name='Bild', blank=True, null=True)
|
||||
cropping = ImageRatioField('image', '1200x800', free_crop=True)
|
||||
|
||||
@@ -50,10 +50,52 @@ class Information(TranslatableModel):
|
||||
title=models.CharField(max_length=100, verbose_name='Title'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Neuigkeit'
|
||||
verbose_name_plural = 'Neuigkeiten'
|
||||
ordering = ['-updated']
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse_lazy('portal:announcement', args=[self.pk])
|
||||
|
||||
|
||||
class InformationSection(TranslatableModel):
|
||||
translations = TranslatedFields(
|
||||
title=models.CharField(max_length=100, verbose_name='Title')
|
||||
)
|
||||
ordering = models.IntegerField(default=50, verbose_name='Sortierung')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Information Section'
|
||||
verbose_name_plural = 'Information Sections'
|
||||
ordering = ['ordering']
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Information(TranslatableModel):
|
||||
section = models.ForeignKey(InformationSection, verbose_name='Information Section', related_name='informations',
|
||||
null=True, blank=True)
|
||||
groups = models.ManyToManyField(Group, verbose_name='Mitglieder Gruppe', related_name='informations')
|
||||
image = CroppableFilerImageField(verbose_name='Bild', blank=True, null=True)
|
||||
cropping = ImageRatioField('image', '1200x800', free_crop=True)
|
||||
|
||||
placeholder = PlaceholderField('content')
|
||||
published = models.BooleanField(verbose_name='Veröffentlicht', default=False)
|
||||
|
||||
translations = TranslatedFields(
|
||||
title=models.CharField(max_length=100, verbose_name='Title'),
|
||||
description=models.TextField(verbose_name='Beschreibung'),
|
||||
tag_list=models.TextField(verbose_name='Tags', help_text='Mit Komma getrennt', null=True, blank=True)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Information'
|
||||
verbose_name_plural = 'Informationen'
|
||||
ordering = ['-updated']
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
@@ -10,7 +10,8 @@ from boto.s3.connection import (
|
||||
|
||||
|
||||
class PrivateS3MediaStorage(S3MediaStorage):
|
||||
def __init__(self):
|
||||
def __init__(self, base_url=None):
|
||||
self.base_url = base_url
|
||||
bucket_name = settings.AWS_MEDIA_STORAGE_BUCKET_NAME
|
||||
|
||||
if '.' in bucket_name:
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<span class="button__icon">{% include 'project/assets/arrow-left-long.svg' %}</span>
|
||||
<span class="button__text">{% trans 'Zurück zur Übersicht' %}</span>
|
||||
</a>
|
||||
{% if not request.user in object.informed_users.all %}
|
||||
{% if not information and not request.user in object.informed_users.all %}
|
||||
<button class="button">
|
||||
<span class="button__icon">{% include 'project/assets/tick.svg' %}</span>
|
||||
<span class="button__text">{% trans 'Als gelesen markieren' %}</span>
|
||||
@@ -92,13 +92,53 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="information" class="downloads__frame">
|
||||
<h2 class="reveal_self reveal reveal_animation">{% trans 'Informationen' %}</h2>
|
||||
<form class="downloads__filter reveal_self reveal reveal_animation"
|
||||
action="{{ request.path }}#information" method="get">
|
||||
<div class="form__field form__field--icon">
|
||||
<input name="information"
|
||||
{% if request.GET.information %}value="{{ request.GET.information }}"{% endif %}
|
||||
class="downloads_search"
|
||||
type="text" placeholder="{% trans 'Suchbegriff eingeben' %}">
|
||||
<button>{% trans 'Suchen' %}{% include 'project/assets/search.svg' %}</button>
|
||||
</div>
|
||||
</form>
|
||||
{% for section in information_sections %}
|
||||
<div class="downloads__section reveal_container">
|
||||
<div class="downloads__section__title{% if flat %} downloads__section__title--flat{% endif %} reveal reveal_animation">
|
||||
<h3>{{ section.title }}</h3>
|
||||
</div>
|
||||
<ul class="downloads">
|
||||
{% for item in section.items %}
|
||||
<li class="reveal_self reveal reveal_animation data_delay_{{ forloop.counter0 }}">
|
||||
<a href="{{ item.get_absolute_url }}" class="download__item download__item--page">
|
||||
<span class="download__item__title">{{ item.title }}</span>
|
||||
<span class="download__item__description">
|
||||
{% if item.description %}{{ item.description }}{% endif %}
|
||||
</span>
|
||||
<span class="downloads__item__tags">
|
||||
{% if item.tag_list %}{{ item.tag_list }}{% endif %}</span>
|
||||
<span class="download__item__icon">
|
||||
{% include 'project/assets/arrow-right.svg' %}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% trans 'Informationen' as information_section_title %}
|
||||
{% include 'project/plugins/content/section_title.html' with section_title=information_section_title %}
|
||||
</div>
|
||||
|
||||
<div id="downloads" class="downloads__frame">
|
||||
<h2 class="reveal_self reveal reveal_animation">{% trans 'Downloads' %}</h2>
|
||||
<form class="downloads__filter reveal_self reveal reveal_animation"
|
||||
action="{{ request.path }}#downloads" method="get">
|
||||
<div class="form__field form__field--icon">
|
||||
<input name="q" {% if request.GET.q %}value="{{ request.GET.q }}"{% endif %}
|
||||
id="downloads_search"
|
||||
class="downloads_search"
|
||||
type="text" placeholder="{% trans 'Suchbegriff eingeben' %}">
|
||||
<button>{% trans 'Suchen' %}{% include 'project/assets/search.svg' %}</button>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ from django.views.generic import TemplateView
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
||||
from portal.forms import LoginForm
|
||||
from portal.views import ProfileEditView, OverviewView, InformationDetailView
|
||||
from portal.views import ProfileEditView, OverviewView, AnnouncementDetailView, InformationDetailView
|
||||
|
||||
urlpatterns = [
|
||||
url(_(r'^login/$'), auth_views.login, {'authentication_form': LoginForm}, name='login'),
|
||||
@@ -30,8 +30,12 @@ urlpatterns = [
|
||||
template_name='portal/edit_success.html'
|
||||
), login_url=reverse_lazy('portal:login')), name='edit_profile_success'),
|
||||
|
||||
url(_(r'^announcement/(?P<pk>\d+)/$'),
|
||||
login_required(AnnouncementDetailView.as_view(), login_url=reverse_lazy('portal:login')), name='announcement'),
|
||||
|
||||
url(_(r'^information/(?P<pk>\d+)/$'),
|
||||
login_required(InformationDetailView.as_view(), login_url=reverse_lazy('portal:login')), name='information'),
|
||||
login_required(InformationDetailView.as_view(), login_url=reverse_lazy('portal:login')),
|
||||
kwargs={'information': True}, name='information'),
|
||||
|
||||
url(_(r'^$'), login_required(OverviewView.as_view(), login_url=reverse_lazy('portal:login')), name='overview'),
|
||||
]
|
||||
|
||||
@@ -5,22 +5,39 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import UpdateView, ListView, DetailView
|
||||
|
||||
from portal.forms import ProfileEditForm
|
||||
from portal.models import Information, DownloadFile
|
||||
from portal.models import DownloadFile, Announcement, Information
|
||||
|
||||
|
||||
class InformationQuerysetMixin(object):
|
||||
class ContentQuerysetMixin(object):
|
||||
def get_queryset(self):
|
||||
queryset = Information.objects.all()
|
||||
queryset = self.model.objects.all()
|
||||
if not self.request.user.is_superuser:
|
||||
queryset = queryset.filter(groups__in=self.request.user.groups.all())
|
||||
queryset = queryset.filter(published=True)
|
||||
return queryset
|
||||
|
||||
|
||||
class OverviewView(InformationQuerysetMixin, ListView):
|
||||
class OverviewView(ContentQuerysetMixin, ListView):
|
||||
template_name = 'portal/overview.html'
|
||||
open_tasks = None
|
||||
paginate_by = 5
|
||||
model = Announcement
|
||||
|
||||
def get_information_sections(self):
|
||||
informations = Information.objects.filter(section__isnull=False, groups__in=self.request.user.groups.all())
|
||||
sections = {}
|
||||
for information in informations:
|
||||
if not sections.get(information.section_id, None):
|
||||
sections[information.section_id] = {
|
||||
'section': information.section,
|
||||
'items': []
|
||||
}
|
||||
sections[information.section_id]['items'].append(information)
|
||||
|
||||
sections_list = [{'title': x['section'].title, 'items': x['items'], 'ordering': x['section'].ordering} for x in
|
||||
sections.values()]
|
||||
|
||||
return sorted(sections_list, key=lambda x: x['ordering'])
|
||||
|
||||
def get_download_sections(self):
|
||||
file_list = DownloadFile.objects.filter(groups__in=self.request.user.groups.all())
|
||||
@@ -43,7 +60,7 @@ class OverviewView(InformationQuerysetMixin, ListView):
|
||||
return queryset.filter(informed_users__in=[self.request.user])
|
||||
|
||||
def get_open_tasks(self):
|
||||
queryset = InformationQuerysetMixin.get_queryset(self).exclude(informed_users__in=[self.request.user])
|
||||
queryset = ContentQuerysetMixin.get_queryset(self).exclude(informed_users__in=[self.request.user])
|
||||
return queryset
|
||||
|
||||
def get_settings(self):
|
||||
@@ -56,6 +73,7 @@ class OverviewView(InformationQuerysetMixin, ListView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(OverviewView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'information_sections': self.get_information_sections(),
|
||||
'download_sections': self.get_download_sections(),
|
||||
'settings': self.get_settings()
|
||||
})
|
||||
@@ -68,14 +86,20 @@ class OverviewView(InformationQuerysetMixin, ListView):
|
||||
return context
|
||||
|
||||
|
||||
class InformationDetailView(InformationQuerysetMixin, DetailView):
|
||||
template_name = 'portal/information.html'
|
||||
class AnnouncementDetailView(ContentQuerysetMixin, DetailView):
|
||||
template_name = 'portal/content.html'
|
||||
model = Announcement
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.get_object().informed_users.add(self.request.user)
|
||||
return redirect('portal:overview')
|
||||
|
||||
|
||||
class InformationDetailView(ContentQuerysetMixin, DetailView):
|
||||
template_name = 'portal/content.html'
|
||||
model = Information
|
||||
|
||||
|
||||
class ProfileEditView(UpdateView):
|
||||
form_class = ProfileEditForm
|
||||
template_name = 'portal/edit_form.html'
|
||||
|
||||
Reference in New Issue
Block a user