##// END OF EJS Templates
Download image in chunks and limit size even if the server has no...
Download image in chunks and limit size even if the server has no content-lenght or lies

File last commit:

r965:b4b69de3 default
r965:b4b69de3 default
Show More
forms.py
331 lines | 10.5 KiB | text/x-python | PythonLexer
neko259
Added a regex for validation of tags. This refs #10
r69 import re
neko259
Added image duplicate check
r527 import time
Ilyas
Added creating new thread form...
r14 from django import forms
neko259
Load image from URL (BB-56)
r954 from django.core.files.uploadedfile import SimpleUploadedFile
neko259
Added form validation, added style for the tag panel. This fixes #13
r76 from django.forms.util import ErrorList
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 from django.utils.translation import ugettext_lazy as _
neko259
Load image from URL (BB-56)
r954 import requests
neko259
Added image duplicate check
r527
neko259
Added a new format panel to the text form
r438 from boards.mdx_neboard import formatters
neko259
Split up user models
r386 from boards.models.post import TITLE_MAX_LENGTH
neko259
Load image from URL (BB-56)
r954 from boards.models import Tag
neko259
Added themes support. Added 'snow white' theme by Mystra_x64.
r35 from neboard import settings
neko259
Moved some settings to boards.settings
r333 import boards.settings as board_settings
neko259
Added form validation, added style for the tag panel. This fixes #13
r76
neko259
Load image from URL (BB-56)
r954
CONTENT_TYPE_IMAGE = (
'image/jpeg',
'image/png',
'image/gif',
'image/bmp',
)
neko259
Removed old forms, refactored some code for forms and images. Use the same code to compute hash of an image in form and when saving image
r937 REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE)
neko259
Added veterans (old users). Veterans currently have lower posting speed limit
r642 VETERAN_POSTING_DELAY = 5
neko259
Added placeholders to forms
r517 ATTRIBUTE_PLACEHOLDER = 'placeholder'
LAST_POST_TIME = 'last_post_time'
LAST_LOGIN_TIME = 'last_login_time'
neko259
Removed old forms, refactored some code for forms and images. Use the same code to compute hash of an image in form and when saving image
r937 TEXT_PLACEHOLDER = _('Type message here. Use formatting panel for more advanced usage.')
neko259
Fixed tags field placeholder. Fixed thread title
r521 TAGS_PLACEHOLDER = _('tag1 several_words_tag')
neko259
Added localizations to forms. Do not allow users without session to post.
r211
neko259
Added image duplicate check
r527 LABEL_TITLE = _('Title')
LABEL_TEXT = _('Text')
neko259
Added post admin page with tags edit capability
r566 LABEL_TAG = _('Tag')
neko259
Use own search form and view
r718 LABEL_SEARCH = _('Search')
neko259
Added post admin page with tags edit capability
r566
TAG_MAX_LENGTH = 20
neko259
Download image in chunks and limit size even if the server has no...
r965 IMAGE_DOWNLOAD_CHUNK_BYTES = 100000
HTTP_RESULT_OK = 200
neko259
Implemented posting delay. User must wait some time before posting (a measure against flood).
r153
neko259
Added a new format panel to the text form
r438 class FormatPanel(forms.Textarea):
neko259
Removed old forms, refactored some code for forms and images. Use the same code to compute hash of an image in form and when saving image
r937 """
Panel for text formatting. Consists of buttons to add different tags to the
form text area.
"""
neko259
Added a new format panel to the text form
r438 def render(self, name, value, attrs=None):
output = '<div id="mark-panel">'
for formatter in formatters:
neko259
Removed 'u' that was indicating a unicode string
r769 output += '<span class="mark_btn"' + \
' onClick="addMarkToMsg(\'' + formatter.format_left + \
neko259
Added a new format panel to the text form
r438 '\', \'' + formatter.format_right + '\')">' + \
formatter.preview_left + formatter.name + \
neko259
Removed 'u' that was indicating a unicode string
r769 formatter.preview_right + '</span>'
neko259
Added a new format panel to the text form
r438
output += '</div>'
output += super(FormatPanel, self).render(name, value, attrs=None)
return output
neko259
Added form validation, added style for the tag panel. This fixes #13
r76 class PlainErrorList(ErrorList):
def __unicode__(self):
return self.as_text()
def as_text(self):
neko259
Removed 'u' that was indicating a unicode string
r769 return ''.join(['(!) %s ' % e for e in self])
neko259
Added form validation, added style for the tag panel. This fixes #13
r76
neko259
Added a text area to the posting form. Added a basic CSS style. Changed threads list a bit.
r16
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 class NeboardForm(forms.Form):
neko259
Removed old forms, refactored some code for forms and images. Use the same code to compute hash of an image in form and when saving image
r937 """
Form with neboard-specific formatting.
"""
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205
neko259
Small style and forms rendering changes
r426 def as_div(self):
neko259
Added a new format panel to the text form
r438 """
Returns this form rendered as HTML <as_div>s.
"""
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 return self._html_output(
neko259
Basic support for the generic forms. Still need to fix some things in the form...
r425 # TODO Do not show hidden rows in the list here
neko259
Updated form to a new style. Fixed mark manel
r680 normal_row='<div class="form-row"><div class="form-label">'
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 '%(label)s'
neko259
Updated form to a new style. Fixed mark manel
r680 '</div></div>'
'<div class="form-row"><div class="form-input">'
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 '%(field)s'
neko259
Updated form to a new style. Fixed mark manel
r680 '</div></div>'
'<div class="form-row">'
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 '%(help_text)s'
'</div>',
neko259
Small style and forms rendering changes
r426 error_row='<div class="form-row">'
'<div class="form-label"></div>'
'<div class="form-errors">%s</div>'
'</div>',
row_ender='</div>',
help_text_html='%s',
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 errors_on_separate_row=True)
neko259
Added posting over ajax
r533 def as_json_errors(self):
errors = []
neko259
Some additional improvements from 2to3
r771 for name, field in list(self.fields.items()):
neko259
Added posting over ajax
r533 if self[name].errors:
errors.append({
'field': name,
'errors': self[name].errors.as_text(),
})
return errors
neko259
Added a new format panel to the text form
r438
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 class PostForm(NeboardForm):
neko259
Added form validation, added style for the tag panel. This fixes #13
r76
neko259
Added some labels to the forms.
r232 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
neko259
Added image duplicate check
r527 label=LABEL_TITLE)
neko259
Added placeholders to forms
r517 text = forms.CharField(
widget=FormatPanel(attrs={ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER}),
neko259
Added image duplicate check
r527 required=False, label=LABEL_TEXT)
neko259
Now image form field only accepts image files
r675 image = forms.ImageField(required=False, label=_('Image'),
neko259
Small code cleanups
r721 widget=forms.ClearableFileInput(
attrs={'accept': 'image/*'}))
neko259
Load image from URL (BB-56)
r954 image_url = forms.CharField(required=False, label=_('Image URL'),
widget=forms.TextInput(
attrs={ATTRIBUTE_PLACEHOLDER:
'http://example.com/image.png'}))
neko259
Implemented form validation. When the form fails validation, showing the index page.
r29
neko259
Added a hidden antispam field to the forms.
r207 # This field is for spam prevention only
neko259
Added some labels to the forms.
r232 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
widget=forms.TextInput(attrs={
'class': 'form-email'}))
neko259
Added a hidden antispam field to the forms.
r207
neko259
Implemented posting delay. User must wait some time before posting (a measure against flood).
r153 session = None
neko259
Added autoban by the hidden field (to ban spammers and prevent them from trying to post anything).
r271 need_to_ban = False
neko259
Implemented posting delay. User must wait some time before posting (a measure against flood).
r153
neko259
Added form validation, added style for the tag panel. This fixes #13
r76 def clean_title(self):
title = self.cleaned_data['title']
if title:
if len(title) > TITLE_MAX_LENGTH:
neko259
Added localizations to forms. Do not allow users without session to post.
r211 raise forms.ValidationError(_('Title must have less than %s '
'characters') %
str(TITLE_MAX_LENGTH))
neko259
Added form validation, added style for the tag panel. This fixes #13
r76 return title
neko259
Implemented form validation. When the form fails validation, showing the index page.
r29 def clean_text(self):
neko259
Strip text and tags before saving
r678 text = self.cleaned_data['text'].strip()
neko259
Implemented form validation. When the form fails validation, showing the index page.
r29 if text:
neko259
Moved some settings to boards.settings
r333 if len(text) > board_settings.MAX_TEXT_LENGTH:
neko259
Added localizations to forms. Do not allow users without session to post.
r211 raise forms.ValidationError(_('Text must have less than %s '
'characters') %
neko259
Moved some settings to boards.settings
r333 str(board_settings
.MAX_TEXT_LENGTH))
neko259
Implemented form validation. When the form fails validation, showing the index page.
r29 return text
def clean_image(self):
image = self.cleaned_data['image']
neko259
Load image from URL (BB-56)
r954
self._validate_image(image)
return image
def clean_image_url(self):
url = self.cleaned_data['image_url']
image = None
if url:
image = self._get_image_from_url(url)
if not image:
raise forms.ValidationError(_('Invalid URL'))
self._validate_image(image)
neko259
Added image duplicate check
r527
neko259
Implemented form validation. When the form fails validation, showing the index page.
r29 return image
def clean(self):
cleaned_data = super(PostForm, self).clean()
neko259
Added localizations to forms. Do not allow users without session to post.
r211 if not self.session:
raise forms.ValidationError('Humans have sessions')
if cleaned_data['email']:
neko259
Added autoban by the hidden field (to ban spammers and prevent them from trying to post anything).
r271 self.need_to_ban = True
neko259
Added localizations to forms. Do not allow users without session to post.
r211 raise forms.ValidationError('A human cannot enter a hidden field')
neko259
Added a hidden antispam field to the forms.
r207
neko259
Fixed validation message. 'text or image must be entered' was shown instead of the read one.
r151 if not self.errors:
self._clean_text_image()
neko259
Fixed validation of image without text. This refs #13
r77
neko259
Implemented posting delay. User must wait some time before posting (a measure against flood).
r153 if not self.errors and self.session:
self._validate_posting_speed()
neko259
Added form validation, added style for the tag panel. This fixes #13
r76 return cleaned_data
neko259
Load image from URL (BB-56)
r954 def get_image(self):
"""
Gets image from file or URL.
"""
image = self.cleaned_data['image']
return image if image else self.cleaned_data['image_url']
neko259
Added form validation, added style for the tag panel. This fixes #13
r76 def _clean_text_image(self):
text = self.cleaned_data.get('text')
neko259
Load image from URL (BB-56)
r954 image = self.get_image()
neko259
Implemented form validation. When the form fails validation, showing the index page.
r29
if (not text) and (not image):
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 error_message = _('Either text or image must be entered.')
neko259
Fixed validation of image without text. This refs #13
r77 self._errors['text'] = self.error_class([error_message])
neko259
Implemented posting delay. User must wait some time before posting (a measure against flood).
r153
neko259
Load image from URL (BB-56)
r954 def _validate_image(self, image):
if image:
if image.size > board_settings.MAX_IMAGE_SIZE:
raise forms.ValidationError(
_('Image must be less than %s bytes')
% str(board_settings.MAX_IMAGE_SIZE))
neko259
Implemented posting delay. User must wait some time before posting (a measure against flood).
r153 def _validate_posting_speed(self):
can_post = True
neko259
Removed user and settings mode. Added settings manager to manage settings and keep them in the session (or any other backend like cookie in the future
r728 posting_delay = settings.POSTING_DELAY
neko259
Added veterans (old users). Veterans currently have lower posting speed limit
r642
neko259
Added settings to limit posting speed. Added message when the form data is sent and response not yet received
r725 if board_settings.LIMIT_POSTING_SPEED and LAST_POST_TIME in \
self.session:
neko259
Implemented posting delay. User must wait some time before posting (a measure against flood).
r153 now = time.time()
last_post_time = self.session[LAST_POST_TIME]
current_delay = int(now - last_post_time)
neko259
Added veterans (old users). Veterans currently have lower posting speed limit
r642 if current_delay < posting_delay:
neko259
Added localizations to forms. Do not allow users without session to post.
r211 error_message = _('Wait %s seconds after last posting') % str(
neko259
Added veterans (old users). Veterans currently have lower posting speed limit
r642 posting_delay - current_delay)
neko259
Implemented posting delay. User must wait some time before posting (a measure against flood).
r153 self._errors['text'] = self.error_class([error_message])
can_post = False
if can_post:
self.session[LAST_POST_TIME] = time.time()
neko259
Implemented form validation. When the form fails validation, showing the index page.
r29
neko259
Load image from URL (BB-56)
r954 def _get_image_from_url(self, url: str) -> SimpleUploadedFile:
"""
Gets an image file from URL.
"""
img_temp = None
try:
# Verify content headers
response_head = requests.head(url, verify=False)
content_type = response_head.headers['content-type']
if content_type in CONTENT_TYPE_IMAGE:
neko259
Allow downloading images from servers that don't provice content-length header
r962 length_header = response_head.headers.get('content-length')
if length_header:
length = int(length_header)
if length > board_settings.MAX_IMAGE_SIZE:
raise forms.ValidationError(
_('Image must be less than %s bytes')
% str(board_settings.MAX_IMAGE_SIZE))
neko259
Load image from URL (BB-56)
r954
# Get the actual content into memory
neko259
Download image in chunks and limit size even if the server has no...
r965 response = requests.get(url, verify=False, stream=True)
neko259
Load image from URL (BB-56)
r954
neko259
Download image in chunks and limit size even if the server has no...
r965 # Download image, stop if the size exceeds limit
size = 0
content = b''
for chunk in response.iter_content(IMAGE_DOWNLOAD_CHUNK_BYTES):
size += len(chunk)
if size > board_settings.MAX_IMAGE_SIZE:
# TODO Dedup this code into a method
raise forms.ValidationError(
_('Image must be less than %s bytes')
% str(board_settings.MAX_IMAGE_SIZE))
content += chunk
neko259
Load image from URL (BB-56)
r954
neko259
Download image in chunks and limit size even if the server has no...
r965 if response.status_code == HTTP_RESULT_OK and content:
neko259
Load image from URL (BB-56)
r954 # Set a dummy file name that will be replaced
# anyway, just keep the valid extension
filename = 'image.' + content_type.split('/')[1]
img_temp = SimpleUploadedFile(filename, content,
content_type)
except Exception:
neko259
Download image in chunks and limit size even if the server has no...
r965 # Just return no image
neko259
Load image from URL (BB-56)
r954 pass
return img_temp
neko259
Implemented form validation. When the form fails validation, showing the index page.
r29
class ThreadForm(PostForm):
neko259
Added some labels to the forms.
r232
neko259
Added placeholders to forms
r517 tags = forms.CharField(
widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}),
neko259
Disallow threads with space-only tags
r679 max_length=100, label=_('Tags'), required=True)
neko259
Changed metadata design. Make space split tags.
r31
def clean_tags(self):
neko259
Strip text and tags before saving
r678 tags = self.cleaned_data['tags'].strip()
neko259
Added a regex for validation of tags. This refs #10
r69
neko259
Removed old forms, refactored some code for forms and images. Use the same code to compute hash of an image in form and when saving image
r937 if not tags or not REGEX_TAGS.match(tags):
neko259
Disallow threads with space-only tags
r679 raise forms.ValidationError(
_('Inappropriate characters in tags.'))
neko259
Changed metadata design. Make space split tags.
r31
neko259
Added required tags. At least one such tag is needed to create a thread. All...
r922 required_tag_exists = False
for tag in tags.split():
tag_model = Tag.objects.filter(name=tag.strip().lower(),
neko259
Removed old forms, refactored some code for forms and images. Use the same code to compute hash of an image in form and when saving image
r937 required=True)
neko259
Added required tags. At least one such tag is needed to create a thread. All...
r922 if tag_model.exists():
required_tag_exists = True
neko259
Removed old forms, refactored some code for forms and images. Use the same code to compute hash of an image in form and when saving image
r937 break
neko259
Added required tags. At least one such tag is needed to create a thread. All...
r922
if not required_tag_exists:
raise forms.ValidationError(_('Need at least 1 required tag.'))
neko259
Changed metadata design. Make space split tags.
r31 return tags
def clean(self):
cleaned_data = super(ThreadForm, self).clean()
neko259
Added themes support. Added 'snow white' theme by Mystra_x64.
r35 return cleaned_data
neko259
Added ability to disable moderator panel. Refactored forms code. Translating forms.
r205 class SettingsForm(NeboardForm):
theme = forms.ChoiceField(choices=settings.THEMES,
label=_('Theme'))
neko259
Added login functionality.
r144
neko259
Use own search form and view
r718 class SearchForm(NeboardForm):
neko259
Added login and logout for moderators
r729 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False)