diff --git a/r2/r2/controllers/api.py b/r2/r2/controllers/api.py index 812a82a2e..2d40df705 100755 --- a/r2/r2/controllers/api.py +++ b/r2/r2/controllers/api.py @@ -51,7 +51,7 @@ from r2.lib.filters import _force_unicode, websafe_json, websafe, spaceCompress from r2.lib.db import queries from r2.lib.db.queries import changed from r2.lib import promote -from r2.lib.media import force_thumbnail, thumbnail_url +from r2.lib.media import force_thumbnail, thumbnail_url, upload_icon from r2.lib.comment_tree import delete_comment from r2.lib import tracking, cssfilter, emailer from r2.lib.subreddit_search import search_reddits @@ -2907,3 +2907,15 @@ class ApiController(RedditController, OAuth2ResourceController): if client: client.deleted = True client._commit() + + @validatedForm(VUser(), + VModhash(), + client=VOAuth2ClientDeveloper(), + icon_file=VLength('file', max_length=1024*32)) + def POST_setappicon(self, form, jquery, client, icon_file): + if client and icon_file: + filename = 'icon-%s' % client._id + client.icon_url = upload_icon(filename, icon_file, (72, 72)) + client._commit() + jquery('#app-icon-%s' % client._id).attr('src', client.icon_url) + form.set_html('.img-status', _('icon uploaded')) diff --git a/r2/r2/lib/db/tdb_cassandra.py b/r2/r2/lib/db/tdb_cassandra.py index a65fd569d..6b0134f2b 100644 --- a/r2/r2/lib/db/tdb_cassandra.py +++ b/r2/r2/lib/db/tdb_cassandra.py @@ -587,6 +587,7 @@ class ThingBase(object): self._dirties[self._timestamp_prop] = now if not updates and not self._deletes: + self._dirties.clear() return # actually write out the changes to the CF diff --git a/r2/r2/lib/media.py b/r2/r2/lib/media.py index 8a5de040e..773828d57 100644 --- a/r2/r2/lib/media.py +++ b/r2/r2/lib/media.py @@ -80,11 +80,12 @@ def filename_to_s3_bucket(file_name): num = ord(file_name[-1]) % len(g.s3_media_buckets) return g.s3_media_buckets[num] -def s3_upload_media(data, file_name, file_type, mime_type, never_expire): +def s3_upload_media(data, file_name, file_type, mime_type, never_expire, + replace=False): bucket = filename_to_s3_bucket(file_name) s3cp.send_file(bucket, file_name+file_type, data, mime_type, never_expire=never_expire, - replace = False, + replace=replace, reduced_redundancy=True) if g.s3_media_direct: return "http://%s/%s/%s%s" % (s3_direct_url, bucket, file_name, file_type) @@ -184,6 +185,19 @@ def force_thumbnail(link, image_data, never_expire=True, file_type=".jpg"): thumb_url = upload_media(image, never_expire=never_expire, file_type=file_type) update_link(link, thumbnail=thumb_url, media_object=None, thumbnail_size=image.size) +def upload_icon(file_name, image_data, size): + assert g.media_store == "s3" + image = str_to_image(image_data) + image.format = 'PNG' + image.thumbnail(size, Image.ANTIALIAS) + icon_data = image_to_str(image) + return s3_upload_media(icon_data, + file_name=file_name, + mime_type='image/png', + file_type='.png', + never_expire=True, + replace=True) + def run(): @g.stats.amqp_processor('scraper_q') def process_link(msg): diff --git a/r2/r2/public/static/css/reddit.css b/r2/r2/public/static/css/reddit.css index 34209c115..4b5934b71 100755 --- a/r2/r2/public/static/css/reddit.css +++ b/r2/r2/public/static/css/reddit.css @@ -5846,16 +5846,12 @@ tr.gold-accent + tr > td { } .developed-app .collapsed { display: none; } -.developed-app img { - height: 64px; - width: 64px; -} .app-details { position: absolute; top: 7px; left: 80px; - height: 64px; + height: 72px; margin-left: 0.5em; vertical-align: top; } @@ -5863,6 +5859,19 @@ tr.gold-accent + tr > td { .app-details h2 { font-size: medium; } .app-details h3 { font-size: x-small; } +.app-icon { + width: 72px; + height: 72px; + line-height: 72px; + white-space: nowrap; + +background: #eee; +} + +.app-icon img { + vertical-align: middle; +} + .app-description { font-size: small; position: absolute; diff --git a/r2/r2/templates/prefapps.html b/r2/r2/templates/prefapps.html index 69501a7cc..46abf5048 100644 --- a/r2/r2/templates/prefapps.html +++ b/r2/r2/templates/prefapps.html @@ -1,6 +1,12 @@ -<%namespace file="utils.html" import="error_field, plain_link" /> +<%namespace file="utils.html" import="error_field, image_upload, plain_link" /> <%namespace file="printablebuttons.html" import="ynbutton" /> +<%def name="icon(app)"> +
+   +
+ + <%def name="developers(app)"> <% devs = app._developers %> %if devs: @@ -25,9 +31,7 @@ %for app in thing.my_apps:
- %if app.icon_url: - - %endif + ${icon(app)}

%if app.about_url: @@ -54,9 +58,7 @@ %for app in thing.developed_apps:
- %if app.icon_url: - - %endif + ${icon(app)}

%if app.about_url: @@ -73,6 +75,7 @@

+ ${image_upload('/api/setappicon', hidden_data=dict(client_id=app._id))}
- - ${_("icon url")} - - - ${error_field("BAD_URL", "icon_url")} - - ${_("redirect uri")} @@ -196,13 +192,6 @@ ${error_field("BAD_URL", "about_url")} - - ${_("icon url")} - - - ${error_field("BAD_URL", "icon_url")} - - ${_("redirect uri")} diff --git a/r2/r2/templates/utils.html b/r2/r2/templates/utils.html index d41f0c93c..104716721 100755 --- a/r2/r2/templates/utils.html +++ b/r2/r2/templates/utils.html @@ -268,7 +268,8 @@ ${unsafe(txt)} <%def name="image_upload(post_target, current_image = None, onsubmit = '', - onchange = '', label = '', form_id = 'image-upload', ask_type = False)"> + onchange = '', label = '', form_id = 'image-upload', + ask_type = False, hidden_data=None)"> %endif + %if hidden_data: + %for name, value in hidden_data.iteritems(): + + %endfor + %endif
%if ask_type: