diff --git a/r2/example.ini b/r2/example.ini
index 9344ac160..2757f99e9 100644
--- a/r2/example.ini
+++ b/r2/example.ini
@@ -352,6 +352,9 @@ static_secure_domain =
# this is for hosts that don't do on-the-fly gzipping (e.g. s3)
static_pre_gzipped = false
static_secure_pre_gzipped = false
+# which s3 bucket to place subreddit styles on (when empty, stylesheets will be served
+# from the local database instead.
+static_stylesheet_bucket =
# -- translator UI --
# enable/disable access to the translation UI in /admin/i18n
diff --git a/r2/r2/controllers/front.py b/r2/r2/controllers/front.py
index cb1e2f5e1..716ec927c 100755
--- a/r2/r2/controllers/front.py
+++ b/r2/r2/controllers/front.py
@@ -22,6 +22,7 @@
from validator import *
from pylons.i18n import _, ungettext
+from pylons.controllers.util import redirect_to
from reddit_base import RedditController, base_listing, paginated_listing, prevent_framing_and_css
from r2 import config
from r2.models import *
@@ -379,17 +380,26 @@ class FrontController(RedditController):
return res
def GET_stylesheet(self):
+ if g.css_killswitch:
+ self.abort404()
+
# de-stale the subreddit object so we don't poison nginx's cache
if not isinstance(c.site, FakeSubreddit):
c.site = Subreddit._byID(c.site._id, data=True, stale=False)
- if hasattr(c.site,'stylesheet_contents') and not g.css_killswitch:
+ if c.site.stylesheet_is_static:
+ # TODO: X-Private-Subreddit?
+ return redirect_to(c.site.stylesheet_url)
+ else:
+ stylesheet_contents = c.site.stylesheet_contents
+
+ if stylesheet_contents:
c.allow_loggedin_cache = True
self.check_modified(c.site,'stylesheet_contents',
private=False, max_age=7*24*60*60,
must_revalidate=False)
c.response_content_type = 'text/css'
- c.response.content = c.site.stylesheet_contents
+ c.response.content = stylesheet_contents
if c.site.type == 'private':
c.response.headers['X-Private-Subreddit'] = 'private'
return c.response
diff --git a/r2/r2/models/subreddit.py b/r2/r2/models/subreddit.py
index 2e1ed278d..a9a08733c 100644
--- a/r2/r2/models/subreddit.py
+++ b/r2/r2/models/subreddit.py
@@ -22,6 +22,9 @@
from __future__ import with_statement
+import base64
+import hashlib
+
from pylons import c, g
from pylons.i18n import _
@@ -40,12 +43,13 @@ from r2.lib.db import tdb_cassandra
from r2.models.wiki import WikiPage
from r2.lib.merge import ConflictException
from r2.lib.cache import CL_ONE
+from r2.lib.contrib.rcssmin import cssmin
+from r2.lib import s3cp
import math
from r2.lib.utils import set_last_modified
from r2.models.wiki import WikiPage
-from md5 import md5
import os.path
import random
@@ -59,7 +63,7 @@ class Subreddit(Thing, Printable):
stylesheet = None,
stylesheet_rtl = None,
stylesheet_contents = '',
- stylesheet_hash = '0',
+ stylesheet_hash = '',
firsttext = strings.firsttext,
header = None,
header_size = None,
@@ -211,6 +215,30 @@ class Subreddit(Thing, Printable):
def moderators(self):
return self.moderator_ids()
+ @property
+ def stylesheet_is_static(self):
+ """Is the subreddit using the newer static file based stylesheets?"""
+ return g.static_stylesheet_bucket and len(self.stylesheet_hash) == 27
+
+ static_stylesheet_prefix = "subreddit-stylesheet/"
+
+ @property
+ def static_stylesheet_name(self):
+ return "".join((self.static_stylesheet_prefix,
+ self.stylesheet_hash,
+ ".css"))
+
+ @property
+ def stylesheet_url(self):
+ from r2.lib.template_helpers import static, get_domain
+
+ if self.stylesheet_is_static:
+ return static(self.static_stylesheet_name)
+ else:
+ return "http://%s/stylesheet.css?v=%s" % (get_domain(cname=False,
+ subreddit=True),
+ self.stylesheet_hash)
+
@property
def stylesheet_contents_user(self):
try:
@@ -340,10 +368,33 @@ class Subreddit(Thing, Printable):
except tdb_cassandra.NotFound:
wiki = WikiPage.create(self, 'config/stylesheet')
wr = wiki.revise(content, previous=prev, author=author, reason=reason, force=force)
- self.stylesheet_contents = parsed
- self.stylesheet_hash = md5(parsed).hexdigest()
- set_last_modified(self, 'stylesheet_contents')
- c.site._commit()
+
+ minified = cssmin(parsed)
+ if minified:
+ if g.static_stylesheet_bucket:
+ digest = hashlib.sha1(minified).digest()
+ self.stylesheet_hash = (base64.urlsafe_b64encode(digest)
+ .rstrip("="))
+
+ s3cp.send_file(g.static_stylesheet_bucket,
+ self.static_stylesheet_name,
+ minified,
+ content_type="text/css",
+ never_expire=True,
+ replace=False,
+ )
+
+ self.stylesheet_contents = ""
+ else:
+ self.stylesheet_hash = hashlib.md5(minified).hexdigest()
+ self.stylesheet_contents = minified
+ set_last_modified(self, 'stylesheet_contents')
+ else:
+ self.stylesheet_contents = ""
+ self.stylesheet_hash = ""
+ self.stylesheet_contents_user = "" # reads from wiki; ensure pg clean
+ self._commit()
+
ModAction.create(self, c.user, action='wikirevise', details='Updated subreddit stylesheet')
return wr
diff --git a/r2/r2/templates/reddit.html b/r2/r2/templates/reddit.html
index f811d5adc..93818dd50 100644
--- a/r2/r2/templates/reddit.html
+++ b/r2/r2/templates/reddit.html
@@ -82,21 +82,11 @@
%endif
%endif
- %if c.can_apply_styles and c.allow_styles and c.site.stylesheet_contents:
- <% inline_stylesheet = (
- len(c.site.stylesheet_contents) < 1024
- and '<' not in c.site.stylesheet_contents) %>
- %if inline_stylesheet:
- ## for very simple stylesheets, we can just include them inline
-
- %else:
-
- %endif
+ %if c.can_apply_styles and c.allow_styles and c.site.stylesheet_hash:
+
%endif
%if getattr(thing, "additional_css", None):