Subreddit settings: mobile icon and banner

Upload and delete endpoints for new mobile subreddit icon and banner.

* icon - square, exactly 240x240px
* banner - 16:9 aspect ratio, min 640x360px, max 1280x720px
This commit is contained in:
Florence Yeun
2015-02-17 13:57:48 -08:00
parent a45218804b
commit ff76c8fc0e
4 changed files with 128 additions and 25 deletions

View File

@@ -2101,6 +2101,53 @@ class ApiController(RedditController):
# reset the status boxes
form.set_text('.img-status', _("deleted"))
@require_oauth2_scope("modconfig")
@validatedForm(VSrModerator(perms='config'),
VModhash())
@api_doc(api_section.subreddits, uses_site=True)
def POST_delete_sr_icon(self, form, jquery):
"""Remove the subreddit's custom mobile icon.
See also: [/api/upload_sr_img](#POST_api_upload_sr_img).
"""
if c.site.icon_img:
c.site.icon_img = None
c.site.icon_size = None
c.site._commit()
ModAction.create(c.site, c.user, action='editsettings',
details='del_icon')
# hide the button which started this
form.find('.delete-img').hide()
# hide the preview box
form.find('.img-preview-container').hide()
# reset the status boxes
form.set_text('.img-status', _("deleted"))
@require_oauth2_scope("modconfig")
@validatedForm(VSrModerator(perms='config'),
VModhash())
@api_doc(api_section.subreddits, uses_site=True)
def POST_delete_sr_banner(self, form, jquery):
"""Remove the subreddit's custom mobile banner.
See also: [/api/upload_sr_img](#POST_api_upload_sr_img).
"""
if c.site.banner_img:
c.site.banner_img = None
c.site.banner_size = None
c.site._commit()
ModAction.create(c.site, c.user, action='editsettings',
details='del_banner')
# hide the button which started this
form.find('.delete-img').hide()
# hide the preview box
form.find('.img-preview-container').hide()
# reset the status boxes
form.set_text('.img-status', _("deleted"))
def GET_upload_sr_img(self, *a, **kw):
"""
@@ -2121,15 +2168,29 @@ class ApiController(RedditController):
img_type = VImageType('img_type'),
form_id = VLength('formid', max_length = 100,
docs={"formid": "(optional) can be ignored"}),
upload_type = VOneOf('upload_type',
('img', 'header', 'icon', 'banner')),
header = VInt('header', max=1, min=0))
@api_doc(api_section.subreddits, uses_site=True)
def POST_upload_sr_img(self, file, header, name, form_id, img_type):
"""Add or replace a subreddit image or custom header logo.
def POST_upload_sr_img(self, file, header, name, form_id, img_type,
upload_type=None):
"""Add or replace a subreddit image, custom header logo, custom mobile
icon, or custom mobile banner.
If the `header` value is `0`, an image for use in the subreddit
stylesheet is uploaded with the name specified in `name`. If the value
of `header` is `1` then the image uploaded will be the subreddit's new
logo and `name` will be ignored.
* If the `upload_type` value is `img`, an image for use in the
subreddit stylesheet is uploaded with the name specified in `name`.
* If the `upload_type` value is `header` then the image uploaded will
be the subreddit's new logo and `name` will be ignored.
* If the `upload_type` value is `icon` then the image uploaded will be
the subreddit's new mobile icon and `name` will be ignored.
* If the `upload_type` value is `banner` then the image uploaded will
be the subreddit's new mobile banner and `name` will be ignored.
For backwards compatibility, if `upload_type` is not specified, the
`header` field will be used instead:
* If the `header` field has value `0`, then `upload_type` is `img`.
* If the `header` field has value `1`, then `upload_type` is `header`.
The `img_type` field specifies whether to store the uploaded image as a
PNG or JPEG.
@@ -2142,30 +2203,49 @@ class ApiController(RedditController):
replaced. This does not affect the stylesheet immediately, but will
take effect the next time the stylesheet is saved.
See also: [/api/delete_sr_img](#POST_api_delete_sr_img) and
[/api/delete_sr_header](#POST_api_delete_sr_header).
See also: [/api/delete_sr_img](#POST_api_delete_sr_img),
[/api/delete_sr_header](#POST_api_delete_sr_header),
[/api/delete_sr_icon](#POST_api_delete_sr_icon), and
[/api/delete_sr_banner](#POST_api_delete_sr_banner).
"""
# default error list (default values will reset the errors in
# the response if no error is raised)
errors = dict(BAD_CSS_NAME = "", IMAGE_ERROR = "")
add_image_to_sr = False
size = None
# for backwards compatibility, map header to upload_type
if upload_type is None:
upload_type = 'header' if header else 'img'
if not header:
add_image_to_sr = True
if not name:
# error if the name wasn't specified and the image was not for a sponsored link or header
# this may also fail if a sponsored image was added and the user is not an admin
errors['BAD_CSS_NAME'] = _("bad image name")
if upload_type == 'img' and not name:
# error if the name wasn't specified and the image was not for a sponsored link or header
# this may also fail if a sponsored image was added and the user is not an admin
errors['BAD_CSS_NAME'] = _("bad image name")
if add_image_to_sr and not c.user_is_admin:
if upload_type == 'img' and not c.user_is_admin:
image_count = wiki.ImagesByWikiPage.get_image_count(
c.site, "config/stylesheet")
if image_count >= g.max_sr_images:
errors['IMAGE_ERROR'] = _("too many images (you only get %d)") % g.max_sr_images
size = str_to_image(file).size
if upload_type == 'icon':
if size != Subreddit.ICON_EXACT_SIZE:
errors['IMAGE_ERROR'] = (
_('must be %dx%d pixels') % Subreddit.ICON_EXACT_SIZE)
elif upload_type == 'banner':
if size[0] * 10 / 16 != size[1] * 10 / 9:
# require precision to one decimal point for aspect ratio
errors['IMAGE_ERROR'] = _('16:9 aspect ratio required')
elif size > Subreddit.BANNER_MAX_SIZE:
errors['IMAGE_ERROR'] = (
_('max %dx%d pixels') % Subreddit.BANNER_MAX_SIZE)
elif size < Subreddit.BANNER_MIN_SIZE:
errors['IMAGE_ERROR'] = (
_('min %dx%d pixels') % Subreddit.BANNER_MIN_SIZE)
if any(errors.values()):
return UploadedImage("", "", "", errors=errors, form_id=form_id).render()
else:
@@ -2176,19 +2256,26 @@ class ApiController(RedditController):
errors['IMAGE_ERROR'] = _("Invalid image or general image error")
return UploadedImage("", "", "", errors=errors, form_id=form_id).render()
size = str_to_image(file).size
if header:
if upload_type == 'img':
wiki.ImagesByWikiPage.add_image(c.site, "config/stylesheet",
name, new_url)
kw = dict(details='upload_image', description=name)
elif upload_type == 'header':
c.site.header = new_url
c.site.header_size = size
c.site._commit()
if add_image_to_sr:
wiki.ImagesByWikiPage.add_image(c.site, "config/stylesheet",
name, new_url)
if header:
kw = dict(details='upload_image_header')
else:
kw = dict(details='upload_image', description=name)
elif upload_type == 'icon':
c.site.icon_img = new_url
c.site.icon_size = size
c.site._commit()
kw = dict(details='upload_image_icon')
elif upload_type == 'banner':
c.site.banner_img = new_url
c.site.banner_size = size
c.site._commit()
kw = dict(details='upload_image_banner')
ModAction.create(c.site, c.user, action='editsettings', **kw)
return UploadedImage(_('saved'), new_url, name,

View File

@@ -202,6 +202,8 @@ class ThingJsonTemplate(JsonTemplate):
class SubredditJsonTemplate(ThingJsonTemplate):
_data_attrs_ = ThingJsonTemplate.data_attrs(
accounts_active="accounts_active",
banner_img="banner_img",
banner_size="banner_size",
collapse_deleted_comments="collapse_deleted_comments",
comment_score_hide_mins="comment_score_hide_mins",
description="description",
@@ -210,6 +212,8 @@ class SubredditJsonTemplate(ThingJsonTemplate):
header_img="header",
header_size="header_size",
header_title="header_title",
icon_img="icon_img",
icon_size="icon_size",
over18="over_18",
public_description="public_description",
public_description_html="public_description_html",

View File

@@ -164,7 +164,11 @@ class ModAction(tdb_cassandra.UuidThing):
'stylesheet': _('stylesheet'),
'del_header': _('delete header image'),
'del_image': _('delete image'),
'del_icon': _('delete icon image'),
'del_banner': _('delete banner image'),
'upload_image_header': _('upload header image'),
'upload_image_icon': _('upload icon image'),
'upload_image_banner': _('upload banner image'),
'upload_image': _('upload image'),
# editflair
'flair_edit': _('add/edit flair'),

View File

@@ -249,6 +249,10 @@ class Subreddit(Thing, Printable, BaseSite):
gilding_server_seconds=0,
contest_mode_upvotes_only=False,
collapse_deleted_comments=False,
icon_img='',
icon_size=None,
banner_img='',
banner_size=None,
hide_ads=False,
)
_essentials = ('type', 'name', 'lang')
@@ -264,6 +268,10 @@ class Subreddit(Thing, Printable, BaseSite):
BASE_SELFTEXT_LENGTH = 15000
ONLY_SELFTEXT_LENGTH = 40000
ICON_EXACT_SIZE = (240, 240)
BANNER_MIN_SIZE = (640, 360)
BANNER_MAX_SIZE = (1280, 720)
valid_types = {
'archived',
'employees_only',