##// END OF EJS Templates
Store images as regular attachments instead of separate model
neko259 -
r1590:0eb7ac3c default
parent child Browse files
Show More
@@ -0,0 +1,1
1 FILE_DIRECTORY = 'files/'
@@ -0,0 +1,42
1 # -*- coding: utf-8 -*-
2 # Generated by Django 1.9.5 on 2016-05-21 07:43
3 from __future__ import unicode_literals
4
5 from boards.signals import generate_thumb
6 from boards.utils import get_extension
7 from django.db import migrations, models
8
9
10 class Migration(migrations.Migration):
11
12 def images_to_attachments(apps, schema_editor):
13 PostImage = apps.get_model('boards', 'PostImage')
14 Attachment = apps.get_model('boards', 'Attachment')
15
16 count = 0
17 images = PostImage.objects.all()
18 for image in images:
19 file_type = get_extension(image.image.name)
20 attachment = Attachment.objects.create(
21 file=image.image.file, mimetype=file_type, hash=image.hash,
22 alias=image.alias)
23 generate_thumb(attachment)
24 count += 1
25 print('Processed {} of {} images'.format(count, len(images)))
26 for post in image.post_images.all():
27 post.attachments.add(attachment)
28
29 image.image.close()
30
31 dependencies = [
32 ('boards', '0046_auto_20160520_2307'),
33 ]
34
35 operations = [
36 migrations.AddField(
37 model_name='attachment',
38 name='alias',
39 field=models.TextField(blank=True, null=True, unique=True),
40 ),
41 migrations.RunPython(images_to_attachments),
42 ]
@@ -0,0 +1,19
1 # -*- coding: utf-8 -*-
2 # Generated by Django 1.9.5 on 2016-05-21 08:25
3 from __future__ import unicode_literals
4
5 from django.db import migrations
6
7
8 class Migration(migrations.Migration):
9
10 dependencies = [
11 ('boards', '0047_attachment_alias'),
12 ]
13
14 operations = [
15 migrations.RemoveField(
16 model_name='post',
17 name='images',
18 ),
19 ]
@@ -0,0 +1,18
1 # -*- coding: utf-8 -*-
2 # Generated by Django 1.9.5 on 2016-05-21 08:29
3 from __future__ import unicode_literals
4
5 from django.db import migrations
6
7
8 class Migration(migrations.Migration):
9
10 dependencies = [
11 ('boards', '0048_remove_post_images'),
12 ]
13
14 operations = [
15 migrations.DeleteModel(
16 name='PostImage',
17 ),
18 ]
@@ -1,5 +1,6
1 from boards.abstracts.settingsmanager import SessionSettingsManager
1 from boards.abstracts.settingsmanager import SessionSettingsManager
2 from boards.models import PostImage
2 from boards.models import Attachment
3
3
4
4 class AttachmentAlias:
5 class AttachmentAlias:
5 def get_image(alias):
6 def get_image(alias):
@@ -17,7 +18,7 class SessionAttachmentAlias(AttachmentA
17
18
18 class ModelAttachmentAlias(AttachmentAlias):
19 class ModelAttachmentAlias(AttachmentAlias):
19 def get_image(self, alias):
20 def get_image(self, alias):
20 return PostImage.objects.filter(alias=alias).first()
21 return Attachment.objects.filter(alias=alias).first()
21
22
22
23
23 def get_image_by_alias(alias, session):
24 def get_image_by_alias(alias, session):
@@ -1,8 +1,8
1 from boards.models.attachment import FILE_TYPES_IMAGE
1 from django.contrib import admin
2 from django.contrib import admin
2 from django.utils.translation import ugettext_lazy as _
3 from django.utils.translation import ugettext_lazy as _
3 from django.core.urlresolvers import reverse
4 from django.core.urlresolvers import reverse
4 from django.db.models import F
5 from boards.models import Post, Tag, Ban, Thread, Banner, Attachment, KeyPair, GlobalId
5 from boards.models import Post, Tag, Ban, Thread, Banner, PostImage, KeyPair, GlobalId
6
6
7
7
8 @admin.register(Post)
8 @admin.register(Post)
@@ -40,11 +40,11 class PostAdmin(admin.ModelAdmin):
40 self.message_user(request, _('{} posters were banned, {} messages were hidden').format(bans, hidden))
40 self.message_user(request, _('{} posters were banned, {} messages were hidden').format(bans, hidden))
41
41
42 def linked_images(self, obj: Post):
42 def linked_images(self, obj: Post):
43 images = obj.images.all()
43 images = obj.attachments.filter(mimetype__in=FILE_TYPES_IMAGE)
44 image_urls = ['<a href="{}"><img src="{}" /></a>'.format(
44 image_urls = ['<a href="{}"><img src="{}" /></a>'.format(
45 reverse('admin:%s_%s_change' % (image._meta.app_label,
45 reverse('admin:%s_%s_change' % (image._meta.app_label,
46 image._meta.model_name),
46 image._meta.model_name),
47 args=[image.id]), image.image.url_200x150) for image in images]
47 args=[image.id]), image.file.url_200x150) for image in images]
48 return ', '.join(image_urls)
48 return ', '.join(image_urls)
49 linked_images.allow_tags = True
49 linked_images.allow_tags = True
50
50
@@ -142,8 +142,8 class BannerAdmin(admin.ModelAdmin):
142 list_display = ('title', 'text')
142 list_display = ('title', 'text')
143
143
144
144
145 @admin.register(PostImage)
145 @admin.register(Attachment)
146 class PostImageAdmin(admin.ModelAdmin):
146 class AttachmentAdmin(admin.ModelAdmin):
147 search_fields = ('alias',)
147 search_fields = ('alias',)
148
148
149
149
@@ -1,46 +1,36
1 import os
1 import os
2 from boards.abstracts.constants import FILE_DIRECTORY
2
3
3 from django.core.management import BaseCommand
4 from django.core.management import BaseCommand
4 from django.db import transaction
5 from django.db import transaction
5 from boards.models import Attachment
6 from boards.models import Attachment
6 from boards.models.attachment import FILES_DIRECTORY
7
7
8 from boards.models.image import IMAGES_DIRECTORY, PostImage, IMAGE_THUMB_SIZE
9 from neboard.settings import MEDIA_ROOT
8 from neboard.settings import MEDIA_ROOT
10
9
11
10
12 __author__ = 'neko259'
11 __author__ = 'neko259'
13
12
13 THUMB_SIZE = (200, 150)
14
14
15
15 class Command(BaseCommand):
16 class Command(BaseCommand):
16 help = 'Remove files whose models were deleted'
17 help = 'Remove files whose models were deleted'
17
18
18 @transaction.atomic
19 @transaction.atomic
19 def handle(self, *args, **options):
20 def handle(self, *args, **options):
20 count = 0
21 thumb_prefix = '.{}x{}'.format(*THUMB_SIZE)
21 thumb_prefix = '.{}x{}'.format(*IMAGE_THUMB_SIZE)
22
23 model_files = os.listdir(MEDIA_ROOT + IMAGES_DIRECTORY)
24 for file in model_files:
25 image_name = file if thumb_prefix not in file else file.replace(thumb_prefix, '')
26 found = PostImage.objects.filter(
27 image=IMAGES_DIRECTORY + image_name).exists()
28
29 if not found:
30 print('Missing {}'.format(image_name))
31 os.remove(MEDIA_ROOT + IMAGES_DIRECTORY + file)
32 count += 1
33 print('Deleted {} image files.'.format(count))
34
22
35 count = 0
23 count = 0
36 model_files = os.listdir(MEDIA_ROOT + FILES_DIRECTORY)
24 model_files = os.listdir(MEDIA_ROOT + FILE_DIRECTORY)
37 for file in model_files:
25 for file in model_files:
38 found = Attachment.objects.filter(file=FILES_DIRECTORY + file)\
26 model_filename = file if thumb_prefix not in file else file.replace(
27 thumb_prefix, '')
28 found = Attachment.objects.filter(file=FILE_DIRECTORY + model_filename)\
39 .exists()
29 .exists()
40
30
41 if not found:
31 if not found:
42 print('Missing {}'.format(file))
32 print('Missing {}'.format(file))
43 os.remove(MEDIA_ROOT + FILES_DIRECTORY + file)
33 os.remove(MEDIA_ROOT + FILE_DIRECTORY + file)
44 count += 1
34 count += 1
45
35
46 print('Deleted {} attachment files.'.format(count))
36 print('Deleted {} attachment files.'.format(count))
@@ -2,7 +2,6
2 from __future__ import unicode_literals
2 from __future__ import unicode_literals
3
3
4 from django.db import models, migrations
4 from django.db import models, migrations
5 import boards.models.image
6 import boards.models.base
5 import boards.models.base
7 import boards.thumbs
6 import boards.thumbs
8
7
@@ -5,7 +5,6 STATUS_ARCHIVE = 'archived'
5
5
6 from boards.models.sync_key import KeyPair
6 from boards.models.sync_key import KeyPair
7 from boards.models.signature import GlobalId, Signature
7 from boards.models.signature import GlobalId, Signature
8 from boards.models.image import PostImage
9 from boards.models.attachment import Attachment
8 from boards.models.attachment import Attachment
10 from boards.models.thread import Thread
9 from boards.models.thread import Thread
11 from boards.models.post import Post
10 from boards.models.post import Post
@@ -1,8 +1,12
1 import boards
2 from boards.models import STATUS_ARCHIVE
3 from django.core.files.images import get_image_dimensions
1 from django.db import models
4 from django.db import models
2
5
3 from boards import utils
6 from boards import utils
4 from boards.models.attachment.viewers import get_viewers, AbstractViewer
7 from boards.models.attachment.viewers import get_viewers, AbstractViewer, \
5 from boards.utils import get_upload_filename, get_file_mimetype, get_extension
8 FILE_TYPES_IMAGE
9 from boards.utils import get_upload_filename, get_extension, cached_result
6
10
7
11
8 class AttachmentManager(models.Manager):
12 class AttachmentManager(models.Manager):
@@ -19,6 +23,13 class AttachmentManager(models.Manager):
19
23
20 return attachment
24 return attachment
21
25
26 def get_random_images(self, count, tags=None):
27 images = self.filter(mimetype__in=FILE_TYPES_IMAGE).exclude(
28 post_attachments__thread__status=STATUS_ARCHIVE)
29 if tags is not None:
30 images = images.filter(post_attachments__threads__tags__in=tags)
31 return images.order_by('?')[:count]
32
22
33
23 class Attachment(models.Model):
34 class Attachment(models.Model):
24 objects = AttachmentManager()
35 objects = AttachmentManager()
@@ -26,6 +37,7 class Attachment(models.Model):
26 file = models.FileField(upload_to=get_upload_filename)
37 file = models.FileField(upload_to=get_upload_filename)
27 mimetype = models.CharField(max_length=50)
38 mimetype = models.CharField(max_length=50)
28 hash = models.CharField(max_length=36)
39 hash = models.CharField(max_length=36)
40 alias = models.TextField(unique=True, null=True, blank=True)
29
41
30 def get_view(self):
42 def get_view(self):
31 file_viewer = None
43 file_viewer = None
@@ -40,3 +52,27 class Attachment(models.Model):
40
52
41 def __str__(self):
53 def __str__(self):
42 return self.file.url
54 return self.file.url
55
56 def get_random_associated_post(self):
57 posts = boards.models.Post.objects.filter(attachments__in=[self])
58 return posts.order_by('?').first()
59
60 @cached_result()
61 def get_size(self):
62 if self.mimetype in FILE_TYPES_IMAGE:
63 return get_image_dimensions(self.file)
64 else:
65 return 200, 150
66
67 def get_thumb_url(self):
68 split = self.file.url.rsplit('.', 1)
69 w, h = 200, 150
70 return '%s.%sx%s.%s' % (split[0], w, h, split[1])
71
72 @cached_result()
73 def get_preview_size(self):
74 if self.mimetype in FILE_TYPES_IMAGE:
75 preview_path = self.file.path.replace('.', '.200x150.')
76 return get_image_dimensions(preview_path)
77 else:
78 return 200, 150
@@ -1,3 +1,4
1 from django.core.files.images import get_image_dimensions
1 from django.template.defaultfilters import filesizeformat
2 from django.template.defaultfilters import filesizeformat
2 from django.contrib.staticfiles.templatetags.staticfiles import static
3 from django.contrib.staticfiles.templatetags.staticfiles import static
3
4
@@ -15,6 +16,13 FILE_TYPES_AUDIO = (
15 'mp3',
16 'mp3',
16 'opus',
17 'opus',
17 )
18 )
19 FILE_TYPES_IMAGE = (
20 'jpeg',
21 'jpg',
22 'png',
23 'bmp',
24 'gif',
25 )
18
26
19 PLAIN_FILE_FORMATS = {
27 PLAIN_FILE_FORMATS = {
20 'pdf': 'pdf',
28 'pdf': 'pdf',
@@ -22,6 +30,9 PLAIN_FILE_FORMATS = {
22 'txt': 'txt',
30 'txt': 'txt',
23 }
31 }
24
32
33 CSS_CLASS_IMAGE = 'image'
34 CSS_CLASS_THUMB = 'thumb'
35
25
36
26 def get_viewers():
37 def get_viewers():
27 return AbstractViewer.__subclasses__()
38 return AbstractViewer.__subclasses__()
@@ -83,3 +94,35 class SvgViewer(AbstractViewer):
83 return '<a class="thumb" href="{}">'\
94 return '<a class="thumb" href="{}">'\
84 '<img class="post-image-preview" width="200" height="150" src="{}" />'\
95 '<img class="post-image-preview" width="200" height="150" src="{}" />'\
85 '</a>'.format(self.file.url, self.file.url)
96 '</a>'.format(self.file.url, self.file.url)
97
98
99 class ImageViewer(AbstractViewer):
100 @staticmethod
101 def supports(file_type):
102 return file_type in FILE_TYPES_IMAGE
103
104 def get_format_view(self):
105 metadata = '{}, {}'.format(self.file.name.split('.')[-1],
106 filesizeformat(self.file.size))
107 width, height = get_image_dimensions(self.file.file)
108 preview_path = self.file.path.replace('.', '.200x150.')
109 pre_width, pre_height = get_image_dimensions(preview_path)
110
111 split = self.file.url.rsplit('.', 1)
112 w, h = 200, 150
113 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
114
115 return '<a class="{}" href="{full}">' \
116 '<img class="post-image-preview"' \
117 ' src="{}"' \
118 ' width="{}"' \
119 ' height="{}"' \
120 ' data-width="{}"' \
121 ' data-height="{}" />' \
122 '</a>' \
123 .format(CSS_CLASS_THUMB,
124 thumb_url,
125 str(pre_width),
126 str(pre_height), str(width), str(height),
127 full=self.file.url, image_meta=metadata)
128
@@ -3,7 +3,8 import uuid
3 import re
3 import re
4 from boards import settings
4 from boards import settings
5 from boards.abstracts.tripcode import Tripcode
5 from boards.abstracts.tripcode import Tripcode
6 from boards.models import PostImage, Attachment, KeyPair, GlobalId
6 from boards.models import Attachment, KeyPair, GlobalId
7 from boards.models.attachment import FILE_TYPES_IMAGE
7 from boards.models.base import Viewable
8 from boards.models.base import Viewable
8 from boards.models.post.export import get_exporter, DIFF_TYPE_JSON
9 from boards.models.post.export import get_exporter, DIFF_TYPE_JSON
9 from boards.models.post.manager import PostManager
10 from boards.models.post.manager import PostManager
@@ -27,8 +28,6 APP_LABEL_BOARDS = 'boards'
27
28
28 BAN_REASON_AUTO = 'Auto'
29 BAN_REASON_AUTO = 'Auto'
29
30
30 IMAGE_THUMB_SIZE = (200, 150)
31
32 TITLE_MAX_LENGTH = 200
31 TITLE_MAX_LENGTH = 200
33
32
34 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]')
33 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]')
@@ -74,8 +73,6 class Post(models.Model, Viewable):
74 text = TextField(blank=True, null=True)
73 text = TextField(blank=True, null=True)
75 _text_rendered = TextField(blank=True, null=True, editable=False)
74 _text_rendered = TextField(blank=True, null=True, editable=False)
76
75
77 images = models.ManyToManyField(PostImage, null=True, blank=True,
78 related_name='post_images', db_index=True)
79 attachments = models.ManyToManyField(Attachment, null=True, blank=True,
76 attachments = models.ManyToManyField(Attachment, null=True, blank=True,
80 related_name='attachment_posts')
77 related_name='attachment_posts')
81
78
@@ -212,8 +209,8 class Post(models.Model, Viewable):
212 def get_search_view(self, *args, **kwargs):
209 def get_search_view(self, *args, **kwargs):
213 return self.get_view(need_op_data=True, *args, **kwargs)
210 return self.get_view(need_op_data=True, *args, **kwargs)
214
211
215 def get_first_image(self) -> PostImage:
212 def get_first_image(self) -> Attachment:
216 return self.images.earliest('id')
213 return self.attachments.filter(mimetype__in=FILE_TYPES_IMAGE).earliest('id')
217
214
218 def set_global_id(self, key_pair=None):
215 def set_global_id(self, key_pair=None):
219 """
216 """
@@ -11,19 +11,11 import boards
11
11
12 from boards.models.user import Ban
12 from boards.models.user import Ban
13 from boards.mdx_neboard import Parser
13 from boards.mdx_neboard import Parser
14 from boards.models import PostImage, Attachment
14 from boards.models import Attachment
15 from boards import utils
15 from boards import utils
16
16
17 __author__ = 'neko259'
17 __author__ = 'neko259'
18
18
19 IMAGE_TYPES = (
20 'jpeg',
21 'jpg',
22 'png',
23 'bmp',
24 'gif',
25 )
26
27 POSTS_PER_DAY_RANGE = 7
19 POSTS_PER_DAY_RANGE = 7
28 NO_IP = '0.0.0.0'
20 NO_IP = '0.0.0.0'
29
21
@@ -186,8 +178,4 class PostManager(models.Manager):
186 list(map(thread.tags.add, tags))
178 list(map(thread.tags.add, tags))
187
179
188 def _add_file_to_post(self, file, post):
180 def _add_file_to_post(self, file, post):
189 file_type = file.name.split('.')[-1].lower()
190 if file_type in IMAGE_TYPES:
191 post.images.add(PostImage.objects.create_with_hash(file))
192 else:
193 post.attachments.add(Attachment.objects.create_with_hash(file))
181 post.attachments.add(Attachment.objects.create_with_hash(file))
@@ -72,16 +72,11 class SyncManager:
72
72
73 global_id = post.global_id
73 global_id = post.global_id
74
74
75 images = post.images.all()
76 attachments = post.attachments.all()
75 attachments = post.attachments.all()
77 if global_id.content:
76 if global_id.content:
78 model.append(et.fromstring(global_id.content))
77 model.append(et.fromstring(global_id.content))
79 if len(images) > 0 or len(attachments) > 0:
78 if len(attachments) > 0:
80 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
79 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
81 for image in images:
82 SyncManager._attachment_to_xml(
83 None, attachment_refs, image.image.file,
84 image.hash, image.image.url)
85 for file in attachments:
80 for file in attachments:
86 SyncManager._attachment_to_xml(
81 SyncManager._attachment_to_xml(
87 None, attachment_refs, file.file.file,
82 None, attachment_refs, file.file.file,
@@ -116,14 +111,10 class SyncManager:
116 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
111 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
117 tripcode.text = post.tripcode
112 tripcode.text = post.tripcode
118
113
119 if len(images) > 0 or len(attachments) > 0:
114 if len(attachments) > 0:
120 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
115 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
121 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
116 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
122
117
123 for image in images:
124 SyncManager._attachment_to_xml(
125 attachments_tag, attachment_refs, image.image.file,
126 image.hash, image.image.url)
127 for file in attachments:
118 for file in attachments:
128 SyncManager._attachment_to_xml(
119 SyncManager._attachment_to_xml(
129 attachments_tag, attachment_refs, file.file.file,
120 attachments_tag, attachment_refs, file.file.file,
@@ -1,10 +1,11
1 import hashlib
1 import hashlib
2 from boards.models.attachment import FILE_TYPES_IMAGE
2 from django.template.loader import render_to_string
3 from django.template.loader import render_to_string
3 from django.db import models
4 from django.db import models
4 from django.db.models import Count
5 from django.db.models import Count
5 from django.core.urlresolvers import reverse
6 from django.core.urlresolvers import reverse
6
7
7 from boards.models import PostImage
8 from boards.models import Attachment
8 from boards.models.base import Viewable
9 from boards.models.base import Viewable
9 from boards.models.thread import STATUS_ACTIVE, STATUS_BUMPLIMIT, STATUS_ARCHIVE
10 from boards.models.thread import STATUS_ACTIVE, STATUS_BUMPLIMIT, STATUS_ARCHIVE
10 from boards.utils import cached_result
11 from boards.utils import cached_result
@@ -107,8 +108,9 class Tag(models.Model, Viewable):
107 return self.description
108 return self.description
108
109
109 def get_random_image_post(self, status=[STATUS_ACTIVE, STATUS_BUMPLIMIT]):
110 def get_random_image_post(self, status=[STATUS_ACTIVE, STATUS_BUMPLIMIT]):
110 posts = boards.models.Post.objects.annotate(images_count=Count(
111 posts = boards.models.Post.objects.filter(attachments__mimetype__in=FILE_TYPES_IMAGE)\
111 'images')).filter(images_count__gt=0, threads__tags__in=[self])
112 .annotate(images_count=Count(
113 'attachments')).filter(images_count__gt=0, threads__tags__in=[self])
112 if status is not None:
114 if status is not None:
113 posts = posts.filter(thread__status__in=status)
115 posts = posts.filter(thread__status__in=status)
114 return posts.order_by('?').first()
116 return posts.order_by('?').first()
@@ -143,5 +145,6 class Tag(models.Model, Viewable):
143 return self.children
145 return self.children
144
146
145 def get_images(self):
147 def get_images(self):
146 return PostImage.objects.filter(post_images__thread__tags__in=[self])\
148 return Attachment.objects.filter(
147 .order_by('-post_images__pub_time') No newline at end of file
149 post_attachments__thread__tags__in=[self]).filter(
150 mimetype__in=FILE_TYPES_IMAGE).order_by('-post_images__pub_time') No newline at end of file
@@ -1,5 +1,6
1 import logging
1 import logging
2 from adjacent import Client
2 from adjacent import Client
3 from boards.models.attachment import FILE_TYPES_IMAGE
3
4
4 from django.db.models import Count, Sum, QuerySet, Q
5 from django.db.models import Count, Sum, QuerySet, Q
5 from django.utils import timezone
6 from django.utils import timezone
@@ -138,8 +139,10 class Thread(models.Model):
138
139
139 @cached_result(key_method=_get_cache_key)
140 @cached_result(key_method=_get_cache_key)
140 def get_images_count(self) -> int:
141 def get_images_count(self) -> int:
141 return self.get_replies().annotate(images_count=Count(
142 return self.get_replies().filter(
142 'images')).aggregate(Sum('images_count'))['images_count__sum']
143 attachments__mimetype__in=FILE_TYPES_IMAGE)\
144 .annotate(images_count=Count(
145 'attachments')).aggregate(Sum('images_count'))['images_count__sum']
143
146
144 def can_bump(self) -> bool:
147 def can_bump(self) -> bool:
145 """
148 """
@@ -181,7 +184,7 class Thread(models.Model):
181 """
184 """
182
185
183 query = self.multi_replies.order_by('pub_time').prefetch_related(
186 query = self.multi_replies.order_by('pub_time').prefetch_related(
184 'images', 'thread', 'attachments')
187 'thread', 'attachments')
185 if view_fields_only:
188 if view_fields_only:
186 query = query.defer('poster_ip')
189 query = query.defer('poster_ip')
187 return query
190 return query
@@ -193,9 +196,9 class Thread(models.Model):
193 """
196 """
194 Gets replies that have at least one image attached
197 Gets replies that have at least one image attached
195 """
198 """
196
199 return self.get_replies(view_fields_only).filter(
197 return self.get_replies(view_fields_only).annotate(images_count=Count(
200 attachments__mimetype__in=FILE_TYPES_IMAGE).annotate(images_count=Count(
198 'images')).filter(images_count__gt=0)
201 'attachments')).filter(images_count__gt=0)
199
202
200 def get_opening_post(self, only_id=False) -> Post:
203 def get_opening_post(self, only_id=False) -> Post:
201 """
204 """
@@ -1,7 +1,9
1 import re
1 import re
2 from boards import thumbs
2 from boards.mdx_neboard import get_parser
3 from boards.mdx_neboard import get_parser
3
4
4 from boards.models import Post, GlobalId
5 from boards.models import Post, GlobalId, Attachment
6 from boards.models.attachment.viewers import FILE_TYPES_IMAGE
5 from boards.models.post import REGEX_NOTIFICATION, REGEX_REPLY,\
7 from boards.models.post import REGEX_NOTIFICATION, REGEX_REPLY,\
6 REGEX_GLOBAL_REPLY
8 REGEX_GLOBAL_REPLY
7 from boards.models.post.manager import post_import_deps
9 from boards.models.post.manager import post_import_deps
@@ -12,6 +14,9 from django.dispatch import receiver
12 from django.utils import timezone
14 from django.utils import timezone
13
15
14
16
17 THUMB_SIZES = ((200, 150),)
18
19
15 @receiver(post_save, sender=Post)
20 @receiver(post_save, sender=Post)
16 def connect_replies(instance, **kwargs):
21 def connect_replies(instance, **kwargs):
17 for reply_number in re.finditer(REGEX_REPLY, instance.get_raw_text()):
22 for reply_number in re.finditer(REGEX_REPLY, instance.get_raw_text()):
@@ -90,3 +95,22 def update_thread_on_delete(instance, **
90 def delete_global_id(instance, **kwargs):
95 def delete_global_id(instance, **kwargs):
91 if instance.global_id and instance.global_id.id:
96 if instance.global_id and instance.global_id.id:
92 instance.global_id.delete()
97 instance.global_id.delete()
98
99
100 @receiver(post_save, sender=Attachment)
101 def generate_thumb(instance, **kwargs):
102 if instance.mimetype in FILE_TYPES_IMAGE:
103 for size in THUMB_SIZES:
104 (w, h) = size
105 split = instance.file.name.rsplit('.', 1)
106 thumb_name = '%s.%sx%s.%s' % (split[0], w, h, split[1])
107
108 if not instance.file.storage.exists(thumb_name):
109 # you can use another thumbnailing function if you like
110 thumb_content = thumbs.generate_thumb(instance.file, size, split[1])
111
112 thumb_name_ = instance.file.storage.save(thumb_name, thumb_content)
113
114 if not thumb_name == thumb_name_:
115 raise ValueError(
116 'There is already a file named %s' % thumb_name_)
@@ -40,11 +40,11
40 <div class="tag_info" style="border-bottom: solid .5ex #{{ tag.get_color }}">
40 <div class="tag_info" style="border-bottom: solid .5ex #{{ tag.get_color }}">
41 {% if random_image_post %}
41 {% if random_image_post %}
42 <div class="tag-image">
42 <div class="tag-image">
43 {% with image=random_image_post.images.first %}
43 {% with image=random_image_post.get_first_image %}
44 <a href="{{ random_image_post.get_absolute_url }}"><img
44 <a href="{{ random_image_post.get_absolute_url }}"><img
45 src="{{ image.image.url_200x150 }}"
45 src="{{ image.get_thumb_url }}"
46 width="{{ image.pre_width }}"
46 width="{{ image.get_preview_size.0 }}"
47 height="{{ image.pre_height }}"
47 height="{{ image.get_preview_size.1 }}"
48 alt="{{ random_image_post.id }}"/></a>
48 alt="{{ random_image_post.id }}"/></a>
49 {% endwith %}
49 {% endwith %}
50 </div>
50 </div>
@@ -26,7 +26,7
26 {% endif %}
26 {% endif %}
27
27
28 {% for image in image_aliases %}
28 {% for image in image_aliases %}
29 {{ image.alias }}: <img src="{{ image.image.url_200x150 }}" /> <br />
29 <div>{{ image.alias }}: {{ image.get_view|safe }}</div>
30 {% endfor %}
30 {% endfor %}
31 </div>
31 </div>
32
32
@@ -23,7 +23,7
23 {% autoescape off %}
23 {% autoescape off %}
24 {{ image.get_view }}
24 {{ image.get_view }}
25 <div class="gallery_image_metadata">
25 <div class="gallery_image_metadata">
26 {{ image.width }}x{{ image.height }}
26 {{ image.get_size.0 }}x{{ image.get_size.1 }}
27 {% image_actions image.image.url request.get_host %}
27 {% image_actions image.image.url request.get_host %}
28 <br />
28 <br />
29 <a href="{{ post.get_absolute_url }}">>>{{ post.id }}</a>
29 <a href="{{ post.get_absolute_url }}">>>{{ post.id }}</a>
@@ -2,6 +2,7
2 This module contains helper functions and helper classes.
2 This module contains helper functions and helper classes.
3 """
3 """
4 import hashlib
4 import hashlib
5 from boards.abstracts.constants import FILE_DIRECTORY
5 from random import random
6 from random import random
6 import time
7 import time
7 import hmac
8 import hmac
@@ -29,10 +30,6 SETTING_ANON_MODE = 'AnonymousMode'
29
30
30 ANON_IP = '127.0.0.1'
31 ANON_IP = '127.0.0.1'
31
32
32 UPLOAD_DIRS ={
33 'PostImage': 'images/',
34 'Attachment': 'files/',
35 }
36 FILE_EXTENSION_DELIMITER = '.'
33 FILE_EXTENSION_DELIMITER = '.'
37
34
38
35
@@ -136,9 +133,7 def get_upload_filename(model_instance,
136 str(int(random() * 1000)),
133 str(int(random() * 1000)),
137 extension)
134 extension)
138
135
139 directory = UPLOAD_DIRS[type(model_instance).__name__]
136 return os.path.join(FILE_DIRECTORY, new_name)
140
141 return os.path.join(directory, new_name)
142
137
143
138
144 def get_file_mimetype(file) -> str:
139 def get_file_mimetype(file) -> str:
@@ -5,7 +5,6 from django.core.paginator import EmptyP
5 from django.db import transaction
5 from django.db import transaction
6 from django.http import Http404
6 from django.http import Http404
7 from django.shortcuts import render, redirect
7 from django.shortcuts import render, redirect
8 import requests
9 from django.utils.decorators import method_decorator
8 from django.utils.decorators import method_decorator
10 from django.views.decorators.csrf import csrf_protect
9 from django.views.decorators.csrf import csrf_protect
11
10
@@ -13,7 +12,7 from boards import utils, settings
13 from boards.abstracts.paginator import get_paginator
12 from boards.abstracts.paginator import get_paginator
14 from boards.abstracts.settingsmanager import get_settings_manager
13 from boards.abstracts.settingsmanager import get_settings_manager
15 from boards.forms import ThreadForm, PlainErrorList
14 from boards.forms import ThreadForm, PlainErrorList
16 from boards.models import Post, Thread, Ban, Tag, PostImage, Banner
15 from boards.models import Post, Thread, Ban, Banner
17 from boards.views.banned import BannedView
16 from boards.views.banned import BannedView
18 from boards.views.base import BaseBoardView, CONTEXT_FORM
17 from boards.views.base import BaseBoardView, CONTEXT_FORM
19 from boards.views.posting_mixin import PostMixin
18 from boards.views.posting_mixin import PostMixin
@@ -1,7 +1,7
1 from django.shortcuts import render
1 from django.shortcuts import render
2 from django.views.generic import View
2 from django.views.generic import View
3
3
4 from boards.models import PostImage
4 from boards.models import Attachment
5
5
6 __author__ = 'neko259'
6 __author__ = 'neko259'
7
7
@@ -16,7 +16,7 class RandomImageView(View):
16 def get(self, request):
16 def get(self, request):
17 params = dict()
17 params = dict()
18
18
19 params[CONTEXT_IMAGES] = PostImage.objects.get_random_images(
19 params[CONTEXT_IMAGES] = Attachment.objects.get_random_images(
20 RANDOM_POST_COUNT)
20 RANDOM_POST_COUNT)
21
21
22 return render(request, TEMPLATE, params)
22 return render(request, TEMPLATE, params)
@@ -8,7 +8,7 from boards.middlewares import SESSION_T
8 from boards.views.base import BaseBoardView, CONTEXT_FORM
8 from boards.views.base import BaseBoardView, CONTEXT_FORM
9 from boards.forms import SettingsForm, PlainErrorList
9 from boards.forms import SettingsForm, PlainErrorList
10 from boards import settings
10 from boards import settings
11 from boards.models import PostImage
11 from boards.models import Attachment
12
12
13 FORM_THEME = 'theme'
13 FORM_THEME = 'theme'
14 FORM_USERNAME = 'username'
14 FORM_USERNAME = 'username'
@@ -43,7 +43,7 class SettingsView(BaseBoardView):
43
43
44 params[CONTEXT_FORM] = form
44 params[CONTEXT_FORM] = form
45 params[CONTEXT_HIDDEN_TAGS] = settings_manager.get_hidden_tags()
45 params[CONTEXT_HIDDEN_TAGS] = settings_manager.get_hidden_tags()
46 params[CONTEXT_IMAGE_ALIASES] = PostImage.objects.exclude(alias='').exclude(alias=None)
46 params[CONTEXT_IMAGE_ALIASES] = Attachment.objects.exclude(alias='').exclude(alias=None)
47
47
48 return render(request, TEMPLATE, params)
48 return render(request, TEMPLATE, params)
49
49
@@ -3,8 +3,8 from django.core.urlresolvers import rev
3
3
4 from boards.abstracts.settingsmanager import get_settings_manager, \
4 from boards.abstracts.settingsmanager import get_settings_manager, \
5 SETTING_FAVORITE_TAGS, SETTING_HIDDEN_TAGS
5 SETTING_FAVORITE_TAGS, SETTING_HIDDEN_TAGS
6 from boards.models import Tag, PostImage
6 from boards.models import Tag
7 from boards.views.all_threads import AllThreadsView, DEFAULT_PAGE
7 from boards.views.all_threads import AllThreadsView
8 from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD
8 from boards.views.mixins import DispatcherMixin, PARAMETER_METHOD
9 from boards.forms import ThreadForm, PlainErrorList
9 from boards.forms import ThreadForm, PlainErrorList
10
10
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now