Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 4 commits
  • 26 files changed
  • 0 commit comments
  • 3 contributors
View
97 apps/twitter/__init__.py
@@ -1,97 +0,0 @@
-import logging
-from uuid import uuid4
-
-from django import http
-from django.core.cache import cache
-
-
-log = logging.getLogger('k')
-
-PREFIX = 'custcare_'
-ACCESS_NAME = PREFIX + 'access'
-REDIRECT_NAME = PREFIX + 'redirect'
-REQUEST_KEY_NAME = PREFIX + 'request_key'
-REQUEST_SECRET_NAME = PREFIX + 'request_secret'
-
-MAX_AGE = 3600
-
-
-def url(request, override=None):
- d = {
- 'scheme': 'https' if request.is_secure() else 'http',
- 'host': request.get_host(),
- 'path': request.get_full_path(),
- }
- if override:
- d.update(override)
-
- return u'%s://%s%s' % (d['scheme'], d['host'], d['path'])
-
-
-def auth_wanted(view_func):
- """Twitter sessions are SSL only, so redirect to SSL if needed."""
- def wrapper(request, *args, **kwargs):
-
- if request.COOKIES.get(REDIRECT_NAME) and not request.is_secure():
- ssl_url = url(request, {'scheme': 'https'})
- return http.HttpResponseRedirect(ssl_url)
-
- return view_func(request, *args, **kwargs)
- return wrapper
-
-
-def auth_required(view_func):
- """Return a HttpResponseBadRequest if not authed."""
- def wrapper(request, *args, **kwargs):
-
- if not request.twitter.authed:
- return http.HttpResponseBadRequest()
-
- return view_func(request, *args, **kwargs)
- return wrapper
-
-
-class Session(object):
- id = None
- key = None
- secret = None
-
- @property
- def cachekey_key(self):
- return '{0}_key_{1}'.format(ACCESS_NAME, self.id)
-
- @property
- def cachekey_secret(self):
- return '{0}_secret_{1}'.format(ACCESS_NAME, self.id)
-
- @property
- def authed(self):
- return bool(self.id and self.key and self.secret)
-
- def __init__(self, key=None, secret=None):
- self.id = uuid4().hex
- self.key = key
- self.secret = secret
-
- @classmethod
- def from_request(cls, request):
- s = cls()
- s.id = request.COOKIES.get(ACCESS_NAME)
- s.key = cache.get(s.cachekey_key)
- s.secret = cache.get(s.cachekey_secret)
- return s
-
- def delete(self, response):
- response.delete_cookie(REDIRECT_NAME)
- response.delete_cookie(ACCESS_NAME)
- cache.delete(self.cachekey_key)
- cache.delete(self.cachekey_secret)
- self.id = None
- self.key = None
- self.secret = None
-
- def save(self, response):
- cache.set(self.cachekey_key, self.key, MAX_AGE)
- cache.set(self.cachekey_secret, self.secret, MAX_AGE)
- response.set_cookie(REDIRECT_NAME, '1', max_age=MAX_AGE)
- response.set_cookie(ACCESS_NAME, self.id, max_age=MAX_AGE, secure=True)
View
94 apps/twitter/middleware.py
@@ -1,94 +0,0 @@
-import logging
-import re
-
-from django import http
-from django.conf import settings
-
-from twitter import *
-import tweepy
-
-
-log = logging.getLogger('k')
-
-
-def validate_token(token):
- return bool(token and (len(token) < 100) and re.search('\w+', token))
-
-
-class SessionMiddleware(object):
-
- def process_request(self, request):
-
- request.twitter = Session.from_request(request)
-
- ssl_url = url(request, {'scheme': 'https'})
- auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY,
- settings.TWITTER_CONSUMER_SECRET,
- ssl_url,
- secure=True)
-
- if request.REQUEST.get('twitter_delete_auth'):
- request.twitter = Session()
- return http.HttpResponseRedirect(url(request))
-
- elif request.twitter.authed:
- auth.set_access_token(request.twitter.key, request.twitter.secret)
- request.twitter.api = tweepy.API(auth)
-
- else:
-
- verifier = request.GET.get('oauth_verifier')
- if verifier:
- # We are completing an OAuth login
-
- request_key = request.COOKIES.get(REQUEST_KEY_NAME)
- request_secret = request.COOKIES.get(REQUEST_SECRET_NAME)
-
- if (validate_token(request_key) and
- validate_token(request_secret)):
- auth.set_request_token(request_key, request_secret)
-
- try:
- auth.get_access_token(verifier)
- except tweepy.TweepError:
- log.warning('Tweepy Error with verifier token')
- pass
- else:
- # Override path to drop query string.
- ssl_url = url(request, {'scheme': 'https',
- 'path': request.path})
- response = http.HttpResponseRedirect(ssl_url)
-
- Session(auth.access_token.key,
- auth.access_token.secret).save(response)
- return response
- else:
- # request tokens didn't validate
- log.warning("Twitter Oauth request tokens didn't validate")
-
- elif request.REQUEST.get('twitter_auth_request'):
- # We are requesting Twitter auth
-
- try:
- redirect_url = auth.get_authorization_url()
- except tweepy.TweepError:
- log.warning('Tweepy error while getting authorization url')
- else:
- response = http.HttpResponseRedirect(redirect_url)
- response.set_cookie(REQUEST_KEY_NAME,
- auth.request_token.key, secure=True)
- response.set_cookie(REQUEST_SECRET_NAME,
- auth.request_token.secret, secure=True)
- return response
-
- def process_response(self, request, response):
- if getattr(request, 'twitter', False):
- if request.REQUEST.get('twitter_delete_auth'):
- request.twitter.delete(response)
-
- if request.twitter.authed:
- response.delete_cookie(REQUEST_KEY_NAME)
- response.delete_cookie(REQUEST_SECRET_NAME)
- request.twitter.save(response)
-
- return response
View
0 apps/twitter/models.py
No changes.
View
12 apps/twitter/tests/test_auth.py
@@ -1,12 +0,0 @@
-from sumo.tests import TestCase
-from sumo.urlresolvers import reverse
-
-
-class TwitterMiddlewareTests(TestCase):
- """Tests for the Twitter auth middleware."""
-
- def test_logout(self):
- """Ensure logout POST request works."""
- landing_url = reverse('customercare.landing', locale='en-US')
- resp = self.client.post(landing_url, {'twitter_delete_auth': 1})
- self.assertRedirects(resp, landing_url)
View
0 apps/upload/__init__.py
No changes.
View
35 apps/upload/forms.py
@@ -1,35 +0,0 @@
-from django import forms
-from django.conf import settings
-from django.core.exceptions import ValidationError
-
-from tower import ugettext_lazy as _lazy
-
-MSG_IMAGE_REQUIRED = _lazy(u'You have not selected an image to upload.')
-MSG_IMAGE_LONG = _lazy(
- u'Please keep the length of your image filename to %(max)s '
- 'characters or less. It is currently %(length)s characters.')
-MSG_IMAGE_EXTENSION = _lazy(u'Please upload an image with one of the '
- u'following extensions: jpg, jpeg, png, gif.')
-ALLOWED_IMAGE_EXTENSIONS = ('jpg', 'jpeg', 'png', 'gif')
-
-
-class ImageAttachmentUploadForm(forms.Form):
- """Image upload form."""
- image = forms.ImageField(error_messages={'required': MSG_IMAGE_REQUIRED,
- 'max_length': MSG_IMAGE_LONG},
- max_length=settings.MAX_FILENAME_LENGTH)
-
- def clean(self):
- c = super(ImageAttachmentUploadForm, self).clean()
- clean_image_extension(c.get('image'))
- return c
-
-
-def clean_image_extension(form_field):
- """Ensure only images of certain extensions can be uploaded."""
- if form_field:
- if '.' not in form_field.name:
- raise ValidationError(MSG_IMAGE_EXTENSION)
- _, ext = form_field.name.rsplit('.', 1)
- if ext.lower() not in ALLOWED_IMAGE_EXTENSIONS:
- raise ValidationError(MSG_IMAGE_EXTENSION)
View
36 apps/upload/models.py
@@ -1,36 +0,0 @@
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.contrib.contenttypes.models import ContentType
-from django.contrib.contenttypes import generic
-from django.db import models
-
-from sumo.helpers import reverse
-from sumo.models import ModelBase
-
-
-class ImageAttachment(ModelBase):
- """An image attached to an object using a generic foreign key"""
- file = models.ImageField(upload_to=settings.IMAGE_UPLOAD_PATH,
- max_length=settings.MAX_FILEPATH_LENGTH)
- thumbnail = models.ImageField(upload_to=settings.THUMBNAIL_UPLOAD_PATH,
- null=True)
- creator = models.ForeignKey(User, related_name='image_attachments')
- content_type = models.ForeignKey(ContentType)
- object_id = models.PositiveIntegerField()
-
- content_object = generic.GenericForeignKey()
-
- def __unicode__(self):
- return self.file.name
-
- def get_absolute_url(self):
- return self.file.url
-
- def thumbnail_if_set(self):
- """Returns self.thumbnail, if set, else self.file"""
- return self.thumbnail if self.thumbnail else self.file
-
- def get_delete_url(self):
- """Returns the URL to delete this object. Assumes the object has an
- id."""
- return reverse('upload.del_image_async', args=[self.id])
View
32 apps/upload/storage.py
@@ -1,32 +0,0 @@
-import hashlib
-import itertools
-import os
-import time
-
-from django.core.files.storage import FileSystemStorage as DjangoStorage
-
-
-class RenameFileStorage(DjangoStorage):
- """Subclass Django's file system storage to add our file naming
- conventions."""
-
- def get_available_name(self, name):
- dir_name, file_name = os.path.split(name)
- file_root, file_ext = os.path.splitext(file_name)
-
- # Set file_root to something we like: clean and all ascii
- md5_sub = hashlib.md5(file_root.encode('utf8')).hexdigest()[0:6]
- file_root = time.strftime('%Y-%m-%d-%H-%M-%S-',
- time.localtime()) + md5_sub
- name = os.path.join(dir_name, file_root + file_ext)
-
- # If the filename already exists, add an underscore and a number
- # (before the file extension, if one exists) to the filename until
- # the generated filename doesn't exist.
- count = itertools.count(1)
- while self.exists(name):
- # file_ext includes the dot.
- name = os.path.join(dir_name, "%s_%s%s" %
- (file_root, count.next(), file_ext))
-
- return name
View
73 apps/upload/tasks.py
@@ -1,73 +0,0 @@
-import logging
-import StringIO
-
-from django.conf import settings
-from django.core.files.base import ContentFile
-
-from PIL import Image
-from celery.task import task
-
-log = logging.getLogger('k.task')
-
-
-@task(rate_limit='15/m')
-def generate_thumbnail(for_obj, from_field, to_field,
- max_size=settings.THUMBNAIL_SIZE):
- """Generate a thumbnail, given a model instance with from and to fields.
-
- Optionally specify a max_size.
-
- """
-
- from_ = getattr(for_obj, from_field)
- to_ = getattr(for_obj, to_field)
-
- log_msg = 'Generating thumbnail for {model} {id}: {from_f} -> {to_f}'
- log.info(log_msg.format(model=for_obj.__class__.__name__, id=for_obj.id,
- from_f=from_field, to_f=to_field))
- thumb_content = _create_image_thumbnail(from_.path, longest_side=max_size)
- file_path = from_.path
- if to_: # Clean up old file before creating new one.
- to_.delete(save=False)
- # Don't modify the object.
- to_.save(file_path, thumb_content, save=False)
- # Use update to avoid race conditions with updating different fields.
- # E.g. when generating two thumbnails for different fields of a single
- # object.
- for_obj.update(**{to_field: to_.name})
-
-
-def _create_image_thumbnail(file_path, longest_side=settings.THUMBNAIL_SIZE):
- """
- Returns a thumbnail file with a set longest side.
- """
- originalImage = Image.open(file_path)
- originalImage = originalImage.convert("RGB")
- file_width, file_height = originalImage.size
-
- width, height = _scale_dimensions(file_width, file_height, longest_side)
- resizedImage = originalImage.resize((width, height), Image.ANTIALIAS)
-
- io = StringIO.StringIO()
- resizedImage.save(io, 'JPEG')
-
- return ContentFile(io.getvalue())
-
-
-def _scale_dimensions(width, height, longest_side=settings.THUMBNAIL_SIZE):
- """
- Returns a tuple (width, height), both smaller than longest side, and
- preserves scale.
- """
-
- if width < longest_side and height < longest_side:
- return (width, height)
-
- if width > height:
- new_width = longest_side
- new_height = (new_width * height) / width
- return (new_width, new_height)
-
- new_height = longest_side
- new_width = (new_height * width) / height
- return (new_width, new_height)
View
61 apps/upload/templates/upload/attachments.html
@@ -1,61 +0,0 @@
-{# vim: set ts=2 et sts=2 sw=2: #}
-{% macro attachment(image, user=None, has_form=True) -%}
- <div class="attachment">
- {% if user and (user == image.creator or
- user.has_perm('upload.delete_attachment')) %}
- {% if has_form %}
- <form class="delete" method="post"
- action="{{ url('upload.del_image_async', image.pk) }}">
- {{ csrf() }}
- <input type="submit" name="delete" class="delete"
- data-url="{{ url('upload.del_image_async', image.pk) }}"
- title="{{ _('Delete this image') }}" value=""/>
- </form>
- {% else %}
- <input type="submit" class="delete" name="delete"
- data-url="{{ url('upload.del_image_async', image.pk) }}"
- title="{{ _('Delete this image') }}" value=""/>
- <noscript>
- <label>{{ _('Delete this image') }}
- <input type="checkbox" name="delete_image" value="{{ image.pk }}"/>
- </label>
- </noscript>
-
- {% endif %}
- {% endif %}
- <a class="image" href="{{ image.file.url }}">
- <img src="{{ image.thumbnail_if_set().url }}"/>
- </a>
- </div>
-{%- endmacro %}
-
-{% macro attachments_form(model_name, object_id, images, settings, user=None) -%}
- <div class="attachments-upload"
- data-post-url={{ url('upload.up_image_async', model_name, object_id)}}>
- <div class="uploaded{% if not images %} empty{% endif %}">
- <div>{{ _('Uploaded images:') }}</div>
- <div class="attachments attachments-list">
- {% for image in images %}
- {{ attachment(image, user, False) }}
- {% endfor %}
- <div class="upload-progress"
- style="height:{{ settings.THUMBNAIL_SIZE }}px;width:{{ settings.THUMBNAIL_SIZE }}px"></div>
- </div>
- <noscript>
- <input type="submit" name="delete_images" class="btn image-delete"
- value="{{ _('Delete selected images') }}"/>
- </noscript>
- </div>
- <div class="add-attachment">
- <label for="id_image">{{ _('Add images:') }}</label>
- <input type="file" id="id_image" name="image" size="30"
- accept="{{ settings.IMAGE_ALLOWED_MIMETYPES }}"
- title="{{ _('Browse for an image to upload.') }}"/>
- <noscript>
- <input type="submit" name="upload_image" class="btn"
- value="{{ _('Upload') }}"/>
- </noscript>
- </div>
- <div class="adding-attachment"></div>
- </div>
-{%- endmacro %}
View
106 apps/upload/tests/__init__.py
@@ -1,106 +0,0 @@
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.core.files import File
-
-from nose.tools import eq_
-
-from questions.models import Question
-from sumo.tests import TestCase
-from upload.models import ImageAttachment
-from upload.storage import RenameFileStorage
-from upload.utils import (create_imageattachment, check_file_size,
- FileTooLargeError)
-
-
-def check_file_info(file_info, name, width, height, delete_url, url,
- thumbnail_url):
- eq_(name, file_info['name'])
- eq_(width, file_info['width'])
- eq_(height, file_info['height'])
- eq_(delete_url, file_info['delete_url'])
- eq_(url, file_info['url'])
- eq_(thumbnail_url, file_info['thumbnail_url'])
-
-
-def get_file_name(name):
- storage = RenameFileStorage()
- return storage.get_available_name(name)
-
-
-class CheckFileSizeTestCase(TestCase):
- """Tests for check_file_size"""
- def test_check_file_size_under(self):
- """No exception should be raised"""
- with open('apps/upload/tests/media/test.jpg') as f:
- up_file = File(f)
- check_file_size(up_file, settings.IMAGE_MAX_FILESIZE)
-
- def test_check_file_size_over(self):
- """FileTooLargeError should be raised"""
- with open('apps/upload/tests/media/test.jpg') as f:
- up_file = File(f)
- fn = lambda: check_file_size(up_file, 0)
- self.assertRaises(FileTooLargeError, fn)
-
-
-class CreateImageAttachmentTestCase(TestCase):
- fixtures = ['users.json', 'questions.json']
-
- def setUp(self):
- super(CreateImageAttachmentTestCase, self).setUp()
- self.user = User.objects.all()[0]
- self.obj = Question.objects.all()[0]
-
- def tearDown(self):
- ImageAttachment.objects.all().delete()
- super(CreateImageAttachmentTestCase, self).tearDown()
-
- def test_create_imageattachment(self):
- """
- An image attachment is created from an uploaded file.
-
- Verifies all appropriate fields are correctly set.
- """
- with open('apps/upload/tests/media/test.jpg') as f:
- up_file = File(f)
- file_info = create_imageattachment(
- {'image': up_file}, self.user, self.obj)
-
- image = ImageAttachment.objects.all()[0]
- check_file_info(
- file_info, name='apps/upload/tests/media/test.jpg',
- width=90, height=120, delete_url=image.get_delete_url(),
- url=image.get_absolute_url(), thumbnail_url=image.thumbnail.url)
-
-
-class FileNameTestCase(TestCase):
- def _match_file_name(self, name, name_end):
- assert name.endswith(name_end), '"%s" does not end with "%s"' % (
- name, name_end)
-
- def test_empty_file_name(self):
- self._match_file_name('', '')
-
- def test_empty_file_name_with_extension(self):
- self._match_file_name(get_file_name('.wtf'), '3f8242')
-
- def test_ascii(self):
- self._match_file_name(get_file_name('some ascii.jpg'), '5959e0.jpg')
-
- def test_ascii_dir(self):
- self._match_file_name(get_file_name('dir1/dir2/some ascii.jpg'),
- '5959e0.jpg')
-
- def test_low_unicode(self):
- self._match_file_name(
- get_file_name('157d9383e6aeba7180378fd8c1d46f80.gif'),
- 'bdaf1a.gif')
-
- def test_high_unicode(self):
- self._match_file_name(get_file_name(u'\u6709\u52b9.jpeg'),
- 'ce1518.jpeg')
-
- def test_full_mixed(self):
- self._match_file_name(
- get_file_name(u'123\xe5\xe5\xee\xe9\xf8\xe7\u6709\u52b9.png'),
- '686c11.png')
View
BIN apps/upload/tests/media/123ascii有効.jpg
Deleted file not rendered
View
BIN ...s__a_really_long_filename_worth_more_than_250_characters__a_really_long_filename_yes_.jpg
Deleted file not rendered
View
BIN apps/upload/tests/media/test.jpg
Deleted file not rendered
View
BIN apps/upload/tests/media/test_invalid.ext
Binary file not shown.
View
BIN apps/upload/tests/media/test_thumb.jpg
Deleted file not rendered
View
37 apps/upload/tests/test_models.py
@@ -1,37 +0,0 @@
-from django.contrib.auth.models import User
-from django.contrib.contenttypes.models import ContentType
-from django.core.files import File
-
-from nose.tools import eq_
-
-from questions.models import Question
-from sumo.tests import TestCase
-from upload.models import ImageAttachment
-from upload.tasks import generate_thumbnail
-
-
-class ImageAttachmentTestCase(TestCase):
- fixtures = ['users.json', 'questions.json']
-
- def setUp(self):
- super(ImageAttachmentTestCase, self).setUp()
- self.user = User.objects.all()[0]
- self.obj = Question.objects.all()[0]
- self.ct = ContentType.objects.get_for_model(self.obj)
-
- def tearDown(self):
- ImageAttachment.objects.all().delete()
- super(ImageAttachmentTestCase, self).tearDown()
-
- def test_thumbnail_if_set(self):
- """thumbnail_if_set() returns self.thumbnail if set, or else returns
- self.file"""
- image = ImageAttachment(content_object=self.obj, creator=self.user)
- with open('apps/upload/tests/media/test.jpg') as f:
- up_file = File(f)
- image.file.save(up_file.name, up_file, save=True)
-
- eq_(image.file, image.thumbnail_if_set())
-
- generate_thumbnail(image, 'file', 'thumbnail')
- eq_(image.thumbnail, image.thumbnail_if_set())
View
117 apps/upload/tests/test_tasks.py
@@ -1,117 +0,0 @@
-import os
-
-from django.conf import settings
-from django.contrib.auth.models import User
-from django.core.files import File
-from django.core.files.images import ImageFile
-
-from nose.tools import eq_
-
-from questions.models import Question
-from sumo.tests import TestCase
-from upload.models import ImageAttachment
-from upload.tasks import (_scale_dimensions, _create_image_thumbnail,
- generate_thumbnail)
-
-
-class ScaleDimensionsTestCase(TestCase):
-
- def test_scale_dimensions_default(self):
- """A square image of exact size is not scaled."""
- ts = settings.THUMBNAIL_SIZE
- (width, height) = _scale_dimensions(ts, ts, ts)
- eq_(ts, width)
- eq_(ts, height)
-
- def test_small(self):
- """A small image is not scaled."""
- ts = settings.THUMBNAIL_SIZE / 2
- (width, height) = _scale_dimensions(ts, ts)
- eq_(ts, width)
- eq_(ts, height)
-
- def test_width_large(self):
- """An image with large width is scaled to width=MAX."""
- ts = 120
- (width, height) = _scale_dimensions(ts * 3 + 10, ts - 1, ts)
- eq_(ts, width)
- eq_(38, height)
-
- def test_large_height(self):
- """An image with large height is scaled to height=MAX."""
- ts = 150
- (width, height) = _scale_dimensions(ts - 2, ts * 2 + 9, ts)
- eq_(71, width)
- eq_(ts, height)
-
- def test_large_both_height(self):
- """An image with both large is scaled to the largest - height."""
- ts = 150
- (width, height) = _scale_dimensions(ts * 2 + 13, ts * 5 + 30, ts)
- eq_(60, width)
- eq_(ts, height)
-
- def test_large_both_width(self):
- """An image with both large is scaled to the largest - width."""
- ts = 150
- (width, height) = _scale_dimensions(ts * 20 + 8, ts * 4 + 36, ts)
- eq_(ts, width)
- eq_(31, height)
-
-
-class CreateThumbnailTestCase(TestCase):
-
- def test_create_image_thumbnail_default(self):
- """A thumbnail is created from an image file."""
- thumb_content = _create_image_thumbnail(
- 'apps/upload/tests/media/test.jpg')
- actual_thumb = ImageFile(thumb_content)
- with open('apps/upload/tests/media/test_thumb.jpg') as f:
- expected_thumb = ImageFile(f)
-
- eq_(expected_thumb.width, actual_thumb.width)
- eq_(expected_thumb.height, actual_thumb.height)
-
-
-class GenerateThumbnail(TestCase):
- fixtures = ['users.json', 'questions.json']
-
- def setUp(self):
- super(GenerateThumbnail, self).setUp()
- self.user = User.objects.all()[0]
- self.obj = Question.objects.all()[0]
-
- def tearDown(self):
- ImageAttachment.objects.all().delete()
-
- def _image_with_thumbnail(self):
- image = ImageAttachment(content_object=self.obj, creator=self.user)
- with open('apps/upload/tests/media/test.jpg') as f:
- up_file = File(f)
- image.file.save(up_file.name, up_file, save=True)
- generate_thumbnail(image, 'file', 'thumbnail')
- return image
-
- def test_generate_thumbnail_default(self):
- """generate_thumbnail creates a thumbnail."""
- image = self._image_with_thumbnail()
-
- eq_(90, image.thumbnail.width)
- eq_(120, image.thumbnail.height)
-
- def test_generate_thumbnail_twice(self):
- """generate_thumbnail replaces old thumbnail."""
- image = self._image_with_thumbnail()
- old_path = image.thumbnail.path
-
- # The thumbnail exists.
- assert os.path.exists(old_path)
- assert os.path.isfile(old_path)
-
- generate_thumbnail(image, 'file', 'thumbnail')
- new_path = image.thumbnail.path
-
- # The thumbnail was replaced.
- eq_(old_path, new_path)
- assert os.path.exists(new_path)
- assert os.path.isfile(new_path)
View
145 apps/upload/tests/test_views.py
@@ -1,145 +0,0 @@
-import json
-
-from django.conf import settings
-
-from nose.tools import eq_
-
-from sumo.tests import post, LocalizingClient, TestCase
-from upload.forms import MSG_IMAGE_LONG
-from upload.models import ImageAttachment
-
-
-class UploadImageTestCase(TestCase):
- fixtures = ['users.json', 'questions.json']
-
- def setUp(self):
- super(UploadImageTestCase, self).setUp()
- self.client = LocalizingClient()
- self.client.login(username='pcraciunoiu', password='testpass')
-
- def tearDown(self):
- ImageAttachment.objects.all().delete()
- super(UploadImageTestCase, self).tearDown()
-
- def test_model_invalid(self):
- """Specifying an invalid model returns 400."""
- r = post(self.client, 'upload.up_image_async', {'image': ''},
- args=['invalid.model', 123])
-
- eq_(400, r.status_code)
- json_r = json.loads(r.content)
- eq_('error', json_r['status'])
- eq_('Model does not exist.', json_r['message'])
-
- def test_object_notexist(self):
- """Upload nothing returns 404 error and html content."""
- r = post(self.client, 'upload.up_image_async', {'image': ''},
- args=['questions.Question', 123])
-
- eq_(404, r.status_code)
- json_r = json.loads(r.content)
- eq_('error', json_r['status'])
- eq_('Object does not exist.', json_r['message'])
-
- def test_empty_image(self):
- """Upload nothing returns 400 error and json content."""
- r = post(self.client, 'upload.up_image_async', {'image': ''},
- args=['questions.Question', 1])
-
- eq_(400, r.status_code)
- json_r = json.loads(r.content)
- eq_('error', json_r['status'])
- eq_('Invalid or no image received.', json_r['message'])
- eq_('You have not selected an image to upload.',
- json_r['errors']['image'][0])
-
- def test_upload_image(self):
- """Uploading an image works."""
- with open('apps/upload/tests/media/test.jpg') as f:
- r = post(self.client, 'upload.up_image_async', {'image': f},
- args=['questions.Question', 1])
-
- eq_(200, r.status_code)
- json_r = json.loads(r.content)
- eq_('success', json_r['status'])
- file = json_r['file']
- eq_('test.jpg', file['name'])
- eq_(90, file['width'])
- eq_(120, file['height'])
- name = '098f6b.jpg'
- message = 'Url "%s" does not contain "%s"' % (file['url'], name)
- assert (name in file['url']), message
-
- eq_(1, ImageAttachment.objects.count())
- image = ImageAttachment.objects.all()[0]
- eq_('pcraciunoiu', image.creator.username)
- eq_(150, image.file.width)
- eq_(200, image.file.height)
- eq_('question', image.content_type.model)
- eq_(1, image.object_id)
-
- def test_upload_unicode_image(self):
- """Uploading an unicode image works."""
- with open(u'apps/upload/tests/media/123ascii\u6709\u52b9.jpg') as f:
- r = post(self.client, 'upload.up_image_async', {'image': f},
- args=['questions.Question', 1])
-
- eq_(200, r.status_code)
- json_r = json.loads(r.content)
- eq_('success', json_r['status'])
-
- def test_delete_image(self):
- """Deleting an uploaded image works."""
- # Upload the image first
- self.test_upload_image()
- im = ImageAttachment.objects.all()[0]
- r = post(self.client, 'upload.del_image_async', args=[im.id])
-
- eq_(200, r.status_code)
- json_r = json.loads(r.content)
- eq_('success', json_r['status'])
- eq_(0, ImageAttachment.objects.count())
-
- def test_invalid_image(self):
- """Make sure invalid files are not accepted as images."""
- with open('apps/upload/__init__.py', 'rb') as f:
- r = post(self.client, 'upload.up_image_async', {'image': f},
- args=['questions.Question', 1])
-
- eq_(400, r.status_code)
- json_r = json.loads(r.content)
- eq_('error', json_r['status'])
- eq_('Invalid or no image received.', json_r['message'])
- eq_('The submitted file is empty.', json_r['errors']['image'][0])
-
- def test_invalid_image_extensions(self):
- """Make sure invalid extensions are not accepted as images."""
- with open('apps/upload/tests/media/test_invalid.ext', 'rb') as f:
- r = post(self.client, 'upload.up_image_async', {'image': f},
- args=['questions.Question', 1])
-
- eq_(400, r.status_code)
- json_r = json.loads(r.content)
- eq_('error', json_r['status'])
- eq_('Invalid or no image received.', json_r['message'])
- eq_('Please upload an image with one of the following extensions: '
- 'jpg, jpeg, png, gif.', json_r['errors']['__all__'][0])
-
- def test_upload_long_filename(self):
- """Uploading an image with a filename that's too long fails."""
- with open('apps/upload/tests/media/a_really_long_filename_worth_'
- 'more_than_250_characters__a_really_long_filename_worth_'
- 'more_than_250_characters__a_really_long_filename_worth_'
- 'more_than_250_characters__a_really_long_filename_worth_'
- 'more_than_250_characters__a_really_long_filename_yes_.jpg')\
- as f:
- r = post(self.client, 'upload.up_image_async', {'image': f},
- args=['questions.Question', 1])
-
- eq_(400, r.status_code)
- json_r = json.loads(r.content)
- eq_('error', json_r['status'])
- eq_('Invalid or no image received.', json_r['message'])
- eq_(MSG_IMAGE_LONG % {'length': 251,
- 'max': settings.MAX_FILENAME_LENGTH},
- json_r['errors']['image'][0])
View
8 apps/upload/urls.py
@@ -1,8 +0,0 @@
-from django.conf.urls.defaults import patterns, url
-
-urlpatterns = patterns('upload.views',
- url(r'^/image/(?P<model_name>\w+\.\w+)/(?P<object_pk>\d+)$',
- 'up_image_async', name='upload.up_image_async'),
- url(r'^/image/delete/(?P<image_id>\d+)$',
- 'del_image_async', name='upload.del_image_async'),
-)
View
76 apps/upload/utils.py
@@ -1,76 +0,0 @@
-from django.conf import settings
-from django.core.files import File
-
-from tower import ugettext_lazy as _lazy
-
-from upload.forms import ImageAttachmentUploadForm
-from upload.models import ImageAttachment
-from upload.tasks import generate_thumbnail, _scale_dimensions
-
-
-def check_file_size(f, max_allowed_size):
- """Check the file size of f is less than max_allowed_size
-
- Raise FileTooLargeError if the check fails.
-
- """
- if f.size > max_allowed_size:
- message = _lazy(u'"%s" is too large (%sKB), the limit is %sKB') % (
- f.name, f.size >> 10, max_allowed_size >> 10)
- raise FileTooLargeError(message)
-
-
-def create_imageattachment(files, user, obj):
- """
- Given an uploaded file, a user and an object, it creates an ImageAttachment
- owned by `user` and attached to `obj`.
- """
- up_file = files.values()[0]
- check_file_size(up_file, settings.IMAGE_MAX_FILESIZE)
-
- image = ImageAttachment(content_object=obj, creator=user)
- image.file.save(up_file.name, File(up_file), save=True)
-
- # Generate thumbnail off thread
- generate_thumbnail.delay(image, 'file', 'thumbnail')
-
- (width, height) = _scale_dimensions(image.file.width, image.file.height)
- return {'name': up_file.name, 'url': image.file.url,
- 'thumbnail_url': image.thumbnail_if_set().url,
- 'width': width, 'height': height,
- 'delete_url': image.get_delete_url()}
-
-
-class FileTooLargeError(Exception):
- pass
-
-
-def upload_imageattachment(request, obj):
- """Uploads image attachments. See upload_media.
-
- Attaches images to the given object, using the create_imageattachment
- callback.
-
- """
- return upload_media(request, ImageAttachmentUploadForm,
- create_imageattachment, obj=obj)
-
-
-def upload_media(request, form_cls, up_file_callback, instance=None, **kwargs):
- """
- Uploads media files and returns a list with information about each media:
- name, url, thumbnail_url, width, height.
-
- Args:
- * request object
- * form class, used to instantiate and validate form for upload
- * callback to save the file given its content and creator
- * extra kwargs will all be passed to the callback
-
- """
- form = form_cls(request.POST, request.FILES)
- if request.method == 'POST' and form.is_valid():
- return up_file_callback(request.FILES, request.user, **kwargs)
- elif not form.is_valid():
- return form.errors
- return None
View
74 apps/upload/views.py
@@ -1,74 +0,0 @@
-import json
-
-from django.views.decorators.http import require_POST
-from django.core.exceptions import ObjectDoesNotExist
-from django.db.models import get_model
-from django.http import (HttpResponse, HttpResponseNotFound,
- HttpResponseBadRequest)
-
-from commonware.decorators import xframe_sameorigin
-from tower import ugettext as _
-
-from access.decorators import has_perm_or_owns_or_403, login_required
-from upload.models import ImageAttachment
-from upload.utils import upload_imageattachment, FileTooLargeError
-
-
-@login_required
-@require_POST
-@xframe_sameorigin
-def up_image_async(request, model_name, object_pk):
- """Upload all images in request.FILES."""
-
- # Lookup the model's content type
- m = get_model(*model_name.split('.'))
- if m is None:
- message = _('Model does not exist.')
- return HttpResponseBadRequest(
- json.dumps({'status': 'error', 'message': message}))
-
- # Then look up the object by pk
- try:
- obj = m.objects.get(pk=object_pk)
- except ObjectDoesNotExist:
- message = _('Object does not exist.')
- return HttpResponseNotFound(
- json.dumps({'status': 'error', 'message': message}))
-
- try:
- file_info = upload_imageattachment(request, obj)
- except FileTooLargeError as e:
- return HttpResponseBadRequest(
- json.dumps({'status': 'error', 'message': e.args[0]}))
-
- if isinstance(file_info, dict) and 'thumbnail_url' in file_info:
- return HttpResponse(
- json.dumps({'status': 'success', 'file': file_info}))
-
- message = _('Invalid or no image received.')
- return HttpResponseBadRequest(
- json.dumps({'status': 'error', 'message': message,
- 'errors': file_info}))
-
-
-@login_required
-@require_POST
-@xframe_sameorigin
-@has_perm_or_owns_or_403('upload.image_upload', 'creator',
- (ImageAttachment, 'id__iexact', 'image_id'),
- (ImageAttachment, 'id__iexact', 'image_id'))
-def del_image_async(request, image_id):
- """Delete an image given its object id."""
- try:
- image = ImageAttachment.objects.get(pk=image_id)
- except ImageAttachment.DoesNotExist:
- message = _('The requested image could not be found.')
- return HttpResponseNotFound(
- json.dumps({'status': 'error', 'message': message}))
-
- image.file.delete()
- if image.thumbnail:
- image.thumbnail.delete()
- image.delete()
-
- return HttpResponse(json.dumps({'status': 'success'}))
View
15 apps/users/forms.py
@@ -9,10 +9,6 @@
from dekicompat.backends import DekiUserBackend
from sumo.widgets import ImageWidget
-# HACK: This breaks avatar upload, but kuma doesn't use that (yet?) or the
-# upload app. This fixes a missing database error.
-#from upload.forms import clean_image_extension
-#from upload.utils import check_file_size, FileTooLargeError
from users.models import Profile
from users.widgets import FacebookURLWidget, TwitterURLWidget
@@ -240,17 +236,6 @@ class Meta(object):
model = Profile
fields = ('avatar',)
- def clean_avatar(self):
- if not ('avatar' in self.cleaned_data and self.cleaned_data['avatar']):
- return self.cleaned_data['avatar']
- try:
- check_file_size(self.cleaned_data['avatar'],
- settings.MAX_AVATAR_FILE_SIZE)
- except FileTooLargeError as e:
- raise forms.ValidationError(e.args[0])
- clean_image_extension(self.cleaned_data.get('avatar'))
- return self.cleaned_data['avatar']
-
class EmailConfirmationForm(forms.Form):
"""A simple form that requires an email address."""
View
3 apps/users/views.py
@@ -28,7 +28,6 @@
from notifications.tasks import claim_watches
from sumo.decorators import ssl_required
from sumo.urlresolvers import reverse, split_path
-from upload.tasks import _create_image_thumbnail
from users.forms import (ProfileForm, AvatarForm, EmailConfirmationForm,
AuthenticationForm, EmailChangeForm,
PasswordResetForm, BrowserIDRegisterForm,
@@ -462,8 +461,6 @@ def edit_avatar(request):
os.unlink(old_avatar_path)
user_profile = form.save()
- content = _create_image_thumbnail(user_profile.avatar.path,
- settings.AVATAR_SIZE)
# Delete uploaded avatar and replace with thumbnail.
name = user_profile.avatar.name
user_profile.avatar.delete()
View
7 settings.py
@@ -508,8 +508,6 @@ def JINJA_CONFIG():
('apps/search/**', 'ignore'),
('apps/sumo/**', 'ignore'),
('apps/tags/**', 'ignore'),
- ('apps/twitter/**', 'ignore'),
- ('apps/upload/**', 'ignore'),
('apps/**.py',
'tower.management.commands.extract.extract_tower_python'),
('**/templates/**.html',
@@ -724,8 +722,6 @@ def JINJA_CONFIG():
# Columns are 250 but this leaves 50 chars for the upload_to prefix
MAX_FILENAME_LENGTH = 200
MAX_FILEPATH_LENGTH = 250
-# Default storage engine - ours does not preserve filenames
-#DEFAULT_FILE_STORAGE = 'upload.storage.RenameFileStorage'
ATTACHMENT_HOST = 'mdn.mozillademos.org'
@@ -820,9 +816,6 @@ def read_only_mode(env):
# Do not change this without also deleting all wiki documents:
WIKI_DEFAULT_LANGUAGE = LANGUAGE_CODE
-TWITTER_CONSUMER_KEY = ''
-TWITTER_CONSUMER_SECRET = ''
-
NOTIFICATIONS_FROM_ADDRESS = 'notifications@support.mozilla.com'
# Anonymous watches must be confirmed.
View
1 urls.py
@@ -29,7 +29,6 @@
#(r'^forums', include('forums.urls')),
#(r'^questions', include('questions.urls')),
#(r'^flagged', include('flagit.urls')),
- #(r'^upload', include('upload.urls')),
# Docs landing page and next-gen kuma wiki
('', include('docs.urls')),

No commit comments for this range

Something went wrong with that request. Please try again.