Completed
Push — master ( ec11e1...681a20 )
by Bart
26s
created

DocumentListing   A

Size/Duplication

Total Lines 8
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
"""
2
Models that extend mezzanine Pages and add JD specific data.
3
"""
4
5
from datetime import datetime
6
import os
7
from string import punctuation
8
import logging
9
10
from PIL import Image
11
12
from django.core.exceptions import ValidationError
13
from django.db import models
14
from django.db.models import Q
15
from django.utils import timezone
16
from django.utils.translation import ugettext_lazy as _
17
from django.utils.encoding import force_text
18
from django.conf import settings
19
20
from mezzanine.blog.models import BlogCategory, BlogPost
21
from mezzanine.core.fields import FileField
22
from mezzanine.core.fields import RichTextField
23
from mezzanine.core.models import Orderable, RichText, SiteRelated
24
from mezzanine.core.models import CONTENT_STATUS_PUBLISHED
25
from mezzanine.pages.models import Page
26
27
logger = logging.getLogger(__name__)
28
29
30
class FooterLinks(SiteRelated):
31
    title = models.CharField(max_length=100, blank=True, default="")
32
33
    def __str__(self):
34
        return self.title
35
36
37
class FooterLink(SiteRelated, Orderable):
38
    title = models.CharField(max_length=100, blank=True, default="")
39
    url = models.CharField(max_length=500, blank=True, default="")
40
    footer_links = models.ForeignKey(FooterLinks, blank=True, null=True)
41
42
    def __str__(self):
43
        return self.title
44
45
46
class FooterInfo(SiteRelated):
47
    title = models.CharField(max_length=100, blank=True, default="")
48
    content = RichTextField()
49
50
    def __str__(self):
51
        return self.title
52
53
54
class Footer(SiteRelated):
55
    links_left = models.OneToOneField(FooterLinks, auto_created=True, related_name="links_left")
56
    links_middle = models.OneToOneField(FooterLinks, auto_created=True, related_name="links_right")
57
    info_right = models.OneToOneField(FooterInfo, auto_created=True)
58
59
    class Meta:
60
        verbose_name = "Footer"
61
        verbose_name_plural = "Footer"
62
63
64
def validate_header_image(imagepath):
65
    """ Validates the resolution of a header image. """
66
    absolute_imagepath = os.path.join(settings.MEDIA_ROOT, str(imagepath))
67
    if not os.path.exists(absolute_imagepath):
68
        raise ValidationError(
69
            'The file for this header does not exist anymore. Please remove or replace this header before saving the page.')
70
    im = Image.open(absolute_imagepath)
71
    width, height = im.size
72
    aspect_ratio = width/height
73
    if aspect_ratio < 2.0:
74
        raise ValidationError('Image aspect ratio should be at least 2 (for example 2000x1000px). The selected image is %i x %i. Please resize the image.' % (width, height))
75
    if width < 1000:
76
        raise ValidationError('Image resolution is too low. It should be at least 1000px wide. The selected image is %i x %i. Please find a larger image.' % (width, height))
77
78
79
class PageHeaderImage(SiteRelated):
80
    """ Page header image. """
81
    name = models.CharField(max_length=1000, blank=True, null=False, default="")
82
    page = models.ForeignKey(Page, blank=False, null=True)
83
    image = FileField(max_length=200, format="Image", validators=[validate_header_image])
84
85
86
class PageItem(SiteRelated):
87
    page = models.ForeignKey(Page, blank=False, null=True)
88
    visible = models.BooleanField(default=True)
89
90
    class Meta:
91
        abstract = True
92
93
94
class SidebarAgenda(PageItem):
95
    SITE = 'SI'
96
    ALL = 'AL'
97
    MAIN = 'MA'
98
    MAIN_AND_SITE = 'SM'
99
100
    EVENT_CHOICES = (
101
        (SITE, 'Site'),
102
        (ALL, 'All'),
103
        (MAIN, 'Main site'),
104
        (MAIN_AND_SITE, 'Main and site'),
105
    )
106
107
    type = models.CharField(max_length=2, choices=EVENT_CHOICES)
108
109
    class Meta:
110
        verbose_name = "Sidebar Agenda Item"
111
112
    def get_name(self):
113
        if self.type == self.MAIN_AND_SITE:
114
            return 'Events for current and main site'
115
        elif self.type == self.ALL:
116
            return 'Events for all sites'
117
        elif self.type == self.SITE:
118
            return 'Events for current site'
119
        elif self.type == self.MAIN:
120
            return 'Events for main site'
121
        assert False
122
123
    def __str__(self):
124
        return self.get_name()
125
126
127
class SidebarTwitter(PageItem):
128
129
    class Meta:
130
        verbose_name = "Sidebar Twitter Item"
131
132
133
class SidebarSocial(PageItem):
134
135
    @property
136
    def urls(self):
137
        smulrs = SocialMediaUrls.objects.all()
138
        if smulrs.exists():
139
            return smulrs[0]
140
        return None
141
142
    class Meta:
143
        verbose_name = "Sidebar Social Media Item"
144
145
146
class SidebarRichText(PageItem):
147
    title = models.CharField(max_length=100, blank=True, default="")
148
    content = RichTextField()
149
150
    class Meta:
151
        verbose_name = "Sidebar RichText Item"
152
153
    def __str__(self):
154
        return self.title
155
156
157
class SidebarLink(PageItem, Orderable):
158
    title = models.CharField(max_length=100, blank=True, default="")
159
    url = models.CharField(max_length=500, blank=True, default="")
160
161
162
class ActionBanner(PageItem):
163
    title = models.CharField(max_length=500, blank=True, default="")
164
    content = RichTextField()
165
    image = FileField(max_length=300, format="Image")
166
    button_title = models.CharField(max_length=500, blank=True, default="")
167
    button_url = models.CharField(max_length=500, blank=True, default="")
168
169
    def __str__(self):
170
        return self.title
171
172
173
def validate_images_aspect_ratio(imagepath, required_aspect_ratio, max_difference):
174
    """ Validates the aspect ratio of an image. """
175
    absolute_imagepath = os.path.join(settings.MEDIA_ROOT, str(imagepath))
176
    im = Image.open(absolute_imagepath)
177
    width, height = im.size
178
    aspect_ratio = width/height
179
    if abs(aspect_ratio - required_aspect_ratio) > max_difference:
180
        raise ValidationError('Image aspect ratio should be %i, selected image is %i x %i. Please resize the image.' % (required_aspect_ratio, width, height))
181
182
183
def validate_vision_image(imagepath):
184
    validate_images_aspect_ratio(imagepath, required_aspect_ratio=1.5, max_difference=0.1)
185
186
187
class VisionPage(Page, RichText):
188
    """
189
    """
190
    image = FileField(max_length=300, format="Image", blank=True, default="", validators=[validate_vision_image])
191
192
    class Meta:
193
        verbose_name = 'Standpunt pagina'
194
        verbose_name_plural = "Standpunt pagina's"
195
196
197
class VisionsPage(Page, RichText):
198
    """
199
    """
200
    vision_pages = models.ManyToManyField(VisionPage, blank=True, verbose_name="Standpunt pagina's")
201
202
    class Meta:
203
        verbose_name = 'Standpunten pagina'
204
        verbose_name_plural = "Standpunten pagina's"
205
206
207
def validate_organisation_image(imagepath):
208
    validate_images_aspect_ratio(imagepath, required_aspect_ratio=1.5, max_difference=0.1)
209
210
211
class OrganisationPartPage(Page, RichText):
212
    """
213
    """
214
    image = FileField(max_length=300, format="Image", blank=True, default="", validators=[validate_organisation_image])
215
216
    class Meta:
217
        verbose_name = 'Organisatie-onderdeel pagina'
218
        verbose_name_plural = "Organisatie-onderdeel pagina's"
219
220
221
class OrganisationPage(Page, RichText):
222
    """
223
    """
224
    organisation_part_pages = models.ManyToManyField(OrganisationPartPage, blank=True, verbose_name="Organisatie onderdelen")
225
226
    class Meta:
227
        verbose_name = 'Organisatie pagina'
228
        verbose_name_plural = "Organisatie pagina's"
229
230
231
class OrganisationMember(SiteRelated):
232
    """
233
    """
234
    name = models.CharField(max_length=200, blank=False, default="")
235
    content = RichTextField()
236
    image = FileField(max_length=300, format="Image", blank=True, default="")
237
    email = models.EmailField(blank=True, default="")
238
    facebook_url = models.URLField(blank=True, default="")
239
    twitter_url = models.URLField(blank=True, default="")
240
241
    class Meta:
242
        verbose_name = 'Organisatie lid'
243
        verbose_name_plural = "Organisatie leden"
244
245
    def __str__(self):
246
        return self.name
247
248
249
class OrganisationPartMember(SiteRelated):
250
    member = models.ForeignKey(OrganisationMember)
251
    organisation_part = models.ForeignKey(OrganisationPartPage, null=True, blank=True)
252
    role = models.CharField(max_length=200, blank=False, default="")
253
254
    class Meta:
255
        verbose_name = 'Organisatie functie'
256
        verbose_name_plural = "Organisatie functies"
257
258
    def __str__(self):
259
        return self.role + ' - ' + self.member.name
260
261
262
class HomePage(Page, RichText):
263
    """
264
    Page model for the site homepage.
265
    Only works properly when url points to the homepage '/' as url.
266
    """
267
    header_title = models.CharField(max_length=300, blank=True, default="")
268
    header_subtitle = models.CharField(max_length=500, blank=True, default="")
269
    news_category = models.ForeignKey(BlogCategory, null=True, blank=True)
270
    vision_pages = models.ManyToManyField(VisionPage, blank=True, verbose_name="Standpunt pagina's")
271
272
    @property
273
    def blog_posts(self):
274
        return get_public_blogposts(self.news_category)
275
276
    class Meta:
277
        verbose_name = 'Home pagina'
278
        verbose_name_plural = "Home pagina's"
279
280
281
class DocumentListing(Page, RichText):
282
    """
283
    Page model for document listing pages.
284
    """
285
286
    class Meta:
287
        verbose_name = "Document Listing"
288
        verbose_name_plural = "Document Listings"
289
290
291
class Document(Orderable):
292
    """
293
    Model for a document in a DocumentListing.
294
    """
295
296
    document_listing = models.ForeignKey(DocumentListing, related_name="documents")
297
    document = FileField(_("Document"), max_length=200, format="Document")
298
    description = models.CharField(_("Description"), max_length=1000, blank=True)
299
300
    def __str__(self):
301
        return self.description
302
303
    class Meta:
304
        verbose_name = "Document"
305
        verbose_name_plural = "Documents"
306
307
    def save(self, *args, **kwargs):
308
        """
309
        If no description is given when created, create one from the
310
        file name.
311
312
        Code copied from mezzanine.galleries.models.GalleryImage
313
        """
314
        if not self.description:
315
            name = force_text(self.document.name)
316
            name = name.rsplit("/", 1)[-1].rsplit(".", 1)[0]
317
            name = name.replace("'", "")
318
            name = "".join([c if c not in punctuation else " " for c in name])
319
            name = "".join([s.upper() if i == 0 or name[i - 1] == " " else s
320
                            for i, s in enumerate(name)])
321
            self.description = name
322
        super(Document, self).save(*args, **kwargs)
323
324
325
class BlogCategoryPage(Page, RichText):
326
    """
327
    Model for a page that displays a list of posts in a single blog category.
328
    """
329
330
    blog_category = models.ForeignKey(BlogCategory, null=False, blank=False)
331
    show_excerpt = models.BooleanField(default=False, null=False, blank=False,
332
                                       help_text='Show only the first paragraph of a blog post.')
333
334
    class Meta:
335
        verbose_name = "Blog categorie pagina"
336
        verbose_name_plural = "Blog categorie pagina's"
337
338
339
def get_public_blogposts(blog_category):
340
    """ Returns all blogposts for a given category that are published and not expired. """
341
    blog_posts = BlogPost.objects.all().filter(categories=blog_category).filter(status=CONTENT_STATUS_PUBLISHED)
342
    return blog_posts.filter(publish_date__lte=timezone.now()).filter(Q(expiry_date__isnull=True)
343
                                                                      | Q(expiry_date__gte=timezone.now()))
344
345
346
class SocialMediaUrls(SiteRelated):
347
    facebook_url = models.URLField(max_length=300, blank=True, default="")
348
    twitter_url = models.URLField(max_length=300, blank=True, default="")
349
    youtube_url = models.URLField(max_length=300, blank=True, default="")
350
    linkedin_url = models.URLField(max_length=300, blank=True, default="")
351
    instagram_url = models.URLField(max_length=300, blank=True, default="")
352
353
    class Meta:
354
        verbose_name = "Social media urls"
355
        verbose_name_plural = "Social media urls"
356
357
358
class WordLidPage(Page, RichText):
359
    """
360
    """
361
362
    class Meta:
363
        verbose_name = 'Word lid pagina'
364
        verbose_name_plural = "Standpunt pagina's"
365
366
367
class ThatsWhyItem(PageItem, Orderable):
368
    title = models.CharField(max_length=100, blank=True, default="")
369
370
    class Meta:
371
        verbose_name = "That's why item"
372
        verbose_name_plural = "That's why items"
373
374
    def __str__(self):
375
        return self.title
376
377