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 |
|
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 |
|
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. |
|
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( |
|
145 | @admin.register(Attachment) | |
146 |
class |
|
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 + FILE |
|
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 + 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 |
|
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) -> |
|
212 | def get_first_image(self) -> Attachment: | |
216 |
return self. |
|
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 |
|
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 |
|
|
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 |
|
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 |
|
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 |
|
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. |
|
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(). |
|
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 |
|
|
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 |
' |
|
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. |
|
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. |
|
45 | src="{{ image.get_thumb_url }}" | |
46 |
width="{{ image. |
|
46 | width="{{ image.get_preview_size.0 }}" | |
47 |
height="{{ image. |
|
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 |
|
|
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. |
|
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, |
|
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 |
|
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] = |
|
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 |
|
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] = |
|
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 |
|
6 | from boards.models import Tag | |
7 |
from boards.views.all_threads import AllThreadsView |
|
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