Restrict usernames to lowercase, digits, and hyphens.
[zzz-floof.git] / floof / controllers / art.py
1 import logging
2
3 from pylons import config, request, response, session, tmpl_context as c, h
4 from pylons.controllers.util import abort, redirect
5 from pylons import url
6 from floof.lib.base import BaseController, render
7
8 log = logging.getLogger(__name__)
9
10 from floof.lib import file_storage as storage
11 from floof.model import Art, Rating, ArtUser
12 from floof.model.art import ArtUserType
13 from floof.model.comments import Discussion
14 from floof.model.users import User, UserRelationship
15
16 import elixir
17 #import magic
18 import os.path
19 import PIL
20 import PIL.Image
21 from sqlalchemy import func
22 from sqlalchemy.exceptions import IntegrityError
23 from sqlalchemy.orm.exc import NoResultFound
24 from wtforms.validators import ValidationError
25 from wtforms import *
26
27
28 class ArtUploadForm(Form):
29 by = TextField('Artists')
30 file = FileField('Upload')
31 url = TextField('Link')
32
33 # TODO: make this general purpose
34 def validate_file(self, field):
35 if field.data == u'':
36 raise ValidationError('File is required')
37
38
39 class ArtController(BaseController):
40 def __before__(self, id=None):
41 super(ArtController, self).__before__()
42 # Awesome refactoring!
43 if id:
44 c.art = h.get_object_or_404(Art, id=id)
45
46 def new(self):
47 """ New Art! """
48 c.form = ArtUploadForm()
49 return render("/art/new.mako")
50
51 # TODO: login required
52 def create(self):
53 c.form = ArtUploadForm(request.params)
54 if not c.form.validate():
55 ## TODO: JavaScript should be added to the upload form so that it is
56 ## impossible to submit the form when it contains any invalid users,
57 ## so this never happens. Only autocompled usernames should be allowed.
58 return render("/art/new.mako")
59
60 # Save the file
61 upload = request.params['file']
62 hash = storage.save_file('art/original', upload.file)
63
64 # Create a thumbnail and a medium view
65 img = PIL.Image.open(
66 config['app_conf']['static_root']
67 + storage.get_path('art/original', hash)[1:]
68 )
69 for subdir, config_size in [('medium', config['medium_size']),
70 ('thumbnail', config['thumbnail_size'])]:
71 path = config['app_conf']['static_root'] + storage.get_path("art/{0}".format(subdir), hash)
72
73 dir, _ = os.path.split(path)
74 if not os.path.exists(dir):
75 os.makedirs(dir)
76
77 size = int(config_size)
78 shrunken_img = img.copy()
79 shrunken_img.thumbnail((size, size), PIL.Image.ANTIALIAS)
80 shrunken_img.save(path, img.format)
81
82 #magicker = magic.Magic(mime=True)
83 buffer = upload.file.read(64)
84 upload.file.seek(0)
85 mimetype = 'image/unknown' #magickery.from_buffer(buffer)
86
87 # XXX Ensure we can actually handle the mimetype
88
89 print mimetype
90 c.art = Art(
91 uploader=c.user,
92 original_filename=upload.filename,
93 hash=hash,
94 mimetype=mimetype,
95 )
96 c.art.discussion = Discussion(count=0)
97
98 # For the moment, cheerfully assume that people are uploading their own
99 # art
100 ArtUser(art=c.art, user=c.user, type=ArtUserType.BY)
101
102
103 try:
104 elixir.session.commit()
105 redirect(url('show_art', id=c.art.id))
106 except IntegrityError:
107 # XXX Check this as early as possible, and clean up the filesystem!
108 # Right now this replaces the original file!
109 hash = c.art.hash
110 elixir.session.rollback()
111 duplicate_art = Art.get_by(hash=hash)
112 h.flash("We already have that one.")
113 redirect(url('show_art', id=duplicate_art.id))
114
115
116
117 def show(self, id):
118 # c.art = h.get_object_or_404(Art, id=id)
119 if c.user:
120 c.your_score = c.art.user_score(c.user)
121 return render("/art/show.mako")
122
123
124 # TODO: login required
125 def rate(self, id):
126 # c.art = h.get_object_or_404(Art, id=id)
127 score = request.params.get("score")
128 if score and score.isnumeric():
129 score = int(score)
130 else:
131 score = Rating.reverse_options.get(score)
132
133 c.art.rate(score, c.user)
134 elixir.session.commit()
135
136 redirect(url('show_art', id=c.art.id))
137
138
139 def watchstream(self, name):
140 """Watchstream for a certain user."""
141 try:
142 c.watching_user = User.query.filter(func.lower(User.name) == name) \
143 .one()
144 except NoResultFound:
145 abort(404)
146
147 # This user has watches which are users which have art
148 # XXX use artist, not uploader
149 c.artwork = Art.query.join(Art.uploader,
150 User.target_of_relationships) \
151 .filter(UserRelationship.user_id == c.watching_user.id)
152
153 return render('/index.mako')