diff --git a/r2/r2/lib/js.py b/r2/r2/lib/js.py index 533927c9f..f942d3e8a 100755 --- a/r2/r2/lib/js.py +++ b/r2/r2/lib/js.py @@ -169,6 +169,13 @@ class StringsSource(Source): self.keys = keys self.prepend = prepend + @staticmethod + def _encoder(obj): + from r2.lib import strings, translation + if isinstance(obj, strings.StringHandler): + return obj.string_dict + raise TypeError + def get_source(self): from pylons.i18n import get_lang from r2.lib import strings, translation @@ -184,7 +191,7 @@ class StringsSource(Source): else: data = dict(strings.strings) - output = self.prepend + json.dumps(data) + "\n" + output = self.prepend + json.dumps(data, default=self._encoder) + "\n" if self.lang: translation.set_lang(old_lang) diff --git a/r2/r2/lib/permissions.py b/r2/r2/lib/permissions.py new file mode 100644 index 000000000..c0e6f1b87 --- /dev/null +++ b/r2/r2/lib/permissions.py @@ -0,0 +1,97 @@ +# The contents of this file are subject to the Common Public Attribution +# License Version 1.0. (the "License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public +# License Version 1.1, but Sections 14 and 15 have been added to cover use of +# software over a computer network and provide for limited attribution for the +# Original Developer. In addition, Exhibit A has been modified to be consistent +# with Exhibit B. +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +# the specific language governing rights and limitations under the License. +# +# The Original Code is reddit. +# +# The Original Developer is the Initial Developer. The Initial Developer of +# the Original Code is reddit Inc. +# +# All portions of the code written by reddit are Copyright (c) 2006-2013 reddit +# Inc. All Rights Reserved. +############################################################################### + +from pylons.i18n import _ + +class PermissionSet(dict): + ALL = 'all' + + info = None + + def __init__(self, *args, **kwargs): + super(PermissionSet, self).__init__(*args, **kwargs) + + @classmethod + def loads(cls, encoded, validate=False): + if not encoded: + return cls() + result = cls(((term[1:], term[0] == '+') + for term in encoded.split(','))) + if result.get(cls.ALL) == False: + del result[cls.ALL] + if validate and not result.is_valid(): + raise ValueError + return result + + def dumps(self): + if self.is_superuser(): + return '+all' + return ','.join('-+'[bool(v)] + k for k, v in sorted(self.iteritems())) + + def is_superuser(self): + return bool(super(PermissionSet, self).get(self.ALL)) + + def is_valid(self): + if not self.info: + return False + for k in self: + if k != self.ALL and k not in self.info: + return False + return True + + def get(self, key, default=None): + if self.info and self.is_superuser(): + return True if key in self.info else default + return super(PermissionSet, self).get(key, default) + + def __getitem__(self, key): + if self.info and self.is_superuser(): + return key in self.info + return super(PermissionSet, self).get(key, False) + + +class ModeratorPermissionSet(PermissionSet): + info = dict( + access=dict( + title=_('access'), + description=_('manage the lists of contributors and banned users'), + ), + config=dict( + title=_('config'), + description=_('edit settings, sidebar, css, and images'), + ), + flair=dict( + title=_('flair'), + description=_('manage user flair, link flair, and flair templates'), + ), + posts=dict( + title=_('posts'), + description=_( + 'use the approve, remove, spam, distinguish, and nsfw buttons'), + ), + ) + + @classmethod + def loads(cls, encoded, **kwargs): + if encoded is None: + return cls(all=True) + return super(ModeratorPermissionSet, cls).loads(encoded, **kwargs) diff --git a/r2/r2/lib/strings.py b/r2/r2/lib/strings.py index 882587a99..1199a81c1 100644 --- a/r2/r2/lib/strings.py +++ b/r2/r2/lib/strings.py @@ -33,6 +33,7 @@ from pylons.i18n import _, ungettext, get_lang import random import babel.numbers +from r2.lib.permissions import ModeratorPermissionSet from r2.lib.translation import set_lang __all__ = ['StringHandler', 'strings', 'PluralManager', 'plurals', @@ -192,6 +193,15 @@ Note: there are a couple of places outside of your subreddit where someone can c missing_credit_city = _("missing city"), missing_credit_state = _("missing state or province"), missing_credit_zip = _("missing zip code"), + + permissions = dict( + info=dict( + moderator=ModeratorPermissionSet.info, + moderator_invite=ModeratorPermissionSet.info, + ), + all_msg=_("full permissions"), + none_msg=_("no permissions"), + ), ) class StringHandler(object): @@ -213,7 +223,7 @@ class StringHandler(object): if isinstance(rval, (str, unicode)): return _(rval) elif isinstance(rval, dict): - return dict((k, _(v)) for k, v in rval.iteritems()) + return StringHandler(**rval) else: raise AttributeError diff --git a/r2/r2/lib/template_helpers.py b/r2/r2/lib/template_helpers.py index 75d5a3da2..e63577c40 100755 --- a/r2/r2/lib/template_helpers.py +++ b/r2/r2/lib/template_helpers.py @@ -144,14 +144,6 @@ def js_config(extra_config=None): "clicktracker_url": g.clicktracker_url, "uitracker_url": g.uitracker_url, "static_root": static(''), - "permissions": { - "info": { - "moderator": ModeratorPermissionSet.info, - "moderator_invite": ModeratorPermissionSet.info, - }, - "all_msg": _("full permissions"), - "none_msg": _("no permissions"), - }, } if extra_config: diff --git a/r2/r2/models/subreddit.py b/r2/r2/models/subreddit.py index 54fd4d606..58c613dea 100644 --- a/r2/r2/models/subreddit.py +++ b/r2/r2/models/subreddit.py @@ -37,6 +37,7 @@ from r2.lib.db.userrel import UserRel from r2.lib.db.operators import lower, or_, and_, desc from r2.lib.errors import UserRequiredException from r2.lib.memoize import memoize +from r2.lib.permissions import ModeratorPermissionSet from r2.lib.utils import tup, interleave_lists, last_modified_multi, flatten from r2.lib.utils import timeago, summarize_markdown from r2.lib.cache import sgm @@ -57,81 +58,6 @@ from r2.models.wiki import WikiPage import os.path import random -class PermissionSet(dict): - ALL = 'all' - - info = None - - def __init__(self, *args, **kwargs): - super(PermissionSet, self).__init__(*args, **kwargs) - - @classmethod - def loads(cls, encoded, validate=False): - if not encoded: - return cls() - result = cls(((term[1:], term[0] == '+') - for term in encoded.split(','))) - if result.get(cls.ALL) == False: - del result[cls.ALL] - if validate and not result.is_valid(): - raise ValueError - return result - - def dumps(self): - if self.is_superuser(): - return '+all' - return ','.join('-+'[bool(v)] + k for k, v in sorted(self.iteritems())) - - def is_superuser(self): - return bool(super(PermissionSet, self).get(self.ALL)) - - def is_valid(self): - if not self.info: - return False - for k in self: - if k != self.ALL and k not in self.info: - return False - return True - - def get(self, key, default=None): - if self.info and self.is_superuser(): - return True if key in self.info else default - return super(PermissionSet, self).get(key, default) - - def __getitem__(self, key): - if self.info and self.is_superuser(): - return key in self.info - return super(PermissionSet, self).get(key, False) - - -class ModeratorPermissionSet(PermissionSet): - info = dict( - access=dict( - title=_('access'), - description=_('manage the lists of contributors and banned users'), - ), - config=dict( - title=_('config'), - description=_('edit settings, sidebar, css, and images'), - ), - flair=dict( - title=_('flair'), - description=_('manage user flair, link flair, and flair templates'), - ), - posts=dict( - title=_('posts'), - description=_( - 'use the approve, remove, spam, distinguish, and nsfw buttons'), - ), - ) - - @classmethod - def loads(cls, encoded, **kwargs): - if encoded is None: - return cls(all=True) - return super(ModeratorPermissionSet, cls).loads(encoded, **kwargs) - - class SubredditExists(Exception): pass class Subreddit(Thing, Printable): diff --git a/r2/r2/public/static/js/ui.js b/r2/r2/public/static/js/ui.js index a1c110cec..634e281b9 100644 --- a/r2/r2/public/static/js/ui.js +++ b/r2/r2/public/static/js/ui.js @@ -212,7 +212,7 @@ r.ui.PermissionEditor = function(el) { var permission_type = params.type var name = params.name this.form_id = permission_type + "-permissions-" + name - this.permission_info = r.config.permissions.info[permission_type] + this.permission_info = r.strings.permissions.info[permission_type] this.sorted_perm_keys = $.map(this.permission_info, function(v, k) { return k }) this.sorted_perm_keys.sort() @@ -235,7 +235,7 @@ r.ui.PermissionEditor.init = function() { }) } activate('body') - for (var permission_type in r.config.permissions.info) { + for (var permission_type in r.strings.permissions.info) { $('.' + permission_type + '-table') .on('insert-row', 'tr', function(e) { activate(this) }) } @@ -290,7 +290,7 @@ r.ui.PermissionEditor.prototype = $.extend(new r.ui.Base(), { update() }) $label.append( - document.createTextNode(r.config.permissions.all_msg)) + document.createTextNode(r.strings.permissions.all_msg)) } else if (info) { $input.change(update) $label.append(document.createTextNode(info.title)) @@ -335,7 +335,7 @@ r.ui.PermissionEditor.prototype = $.extend(new r.ui.Base(), { var info = this.permission_info[perm] var text if (perm == "all") { - text = r.config.permissions.all_msg + text = r.strings.permissions.all_msg } else if (info) { text = info.title } else { @@ -388,7 +388,7 @@ r.ui.PermissionEditor.prototype = $.extend(new r.ui.Base(), { } if (!spans.length) { spans.push($('') - .text(r.config.permissions.none_msg) + .text(r.strings.permissions.none_msg) .addClass("none")) } var $new_summary = $('
') diff --git a/r2/r2/tests/unit/lib/permissions_test.py b/r2/r2/tests/unit/lib/permissions_test.py new file mode 100644 index 000000000..1c31aee04 --- /dev/null +++ b/r2/r2/tests/unit/lib/permissions_test.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +import unittest + +from r2.tests import stage_for_paste + +stage_for_paste() + +from r2.lib.permissions import PermissionSet, ModeratorPermissionSet + +class TestPermissionSet(PermissionSet): + info = dict(x={}, y={}) + +class PermissionSetTest(unittest.TestCase): + def test_dumps(self): + self.assertEquals( + '+all', PermissionSet(all=True).dumps()) + self.assertEquals( + '+all', PermissionSet(all=True, other=True).dumps()) + self.assertEquals( + '+a,-b', PermissionSet(a=True, b=False).dumps()) + + def test_loads(self): + self.assertEquals("", TestPermissionSet.loads(None).dumps()) + self.assertEquals("", TestPermissionSet.loads("").dumps()) + self.assertEquals("+x,+y", TestPermissionSet.loads("+x,+y").dumps()) + self.assertEquals("+x,-y", TestPermissionSet.loads("+x,-y").dumps()) + self.assertEquals("+all", TestPermissionSet.loads("+x,-y,+all").dumps()) + self.assertEquals("+x,-y,+z", + TestPermissionSet.loads("+x,-y,+z").dumps()) + self.assertRaises(ValueError, + TestPermissionSet.loads, "+x,-y,+z", validate=True) + self.assertEquals( + "+x,-y", + TestPermissionSet.loads("-all,+x,-y", validate=True).dumps()) + + def test_is_superuser(self): + perm_set = PermissionSet() + self.assertFalse(perm_set.is_superuser()) + perm_set[perm_set.ALL] = True + self.assertTrue(perm_set.is_superuser()) + perm_set[perm_set.ALL] = False + self.assertFalse(perm_set.is_superuser()) + + def test_is_valid(self): + perm_set = PermissionSet() + self.assertFalse(perm_set.is_valid()) + + perm_set = TestPermissionSet() + self.assertTrue(perm_set.is_valid()) + perm_set['x'] = True + self.assertTrue(perm_set.is_valid()) + perm_set[perm_set.ALL] = True + self.assertTrue(perm_set.is_valid()) + perm_set['z'] = True + self.assertFalse(perm_set.is_valid()) + + def test_getitem(self): + perm_set = PermissionSet() + perm_set[perm_set.ALL] = True + self.assertFalse(perm_set['x']) + + perm_set = TestPermissionSet() + perm_set['x'] = True + self.assertTrue(perm_set['x']) + self.assertFalse(perm_set['y']) + perm_set['x'] = False + self.assertFalse(perm_set['x']) + perm_set[perm_set.ALL] = True + self.assertTrue(perm_set['x']) + self.assertTrue(perm_set['y']) + self.assertFalse(perm_set['z']) + self.assertTrue(perm_set.get('x', False)) + self.assertFalse(perm_set.get('z', False)) + self.assertTrue(perm_set.get('z', True)) + + +class ModeratorPermissionSetTest(unittest.TestCase): + def test_loads(self): + self.assertTrue(ModeratorPermissionSet.loads(None).is_superuser()) + self.assertFalse(ModeratorPermissionSet.loads('').is_superuser()) + diff --git a/r2/r2/tests/unit/models/subreddit_test.py b/r2/r2/tests/unit/models/subreddit_test.py index a8a119952..caf4837b1 100644 --- a/r2/r2/tests/unit/models/subreddit_test.py +++ b/r2/r2/tests/unit/models/subreddit_test.py @@ -2,86 +2,14 @@ import unittest +from r2.lib.permissions import PermissionSet + from r2.models.account import Account -from r2.models.subreddit import ( - ModeratorPermissionSet, - PermissionSet, - SRMember, - Subreddit, -) +from r2.models.subreddit import SRMember, Subreddit class TestPermissionSet(PermissionSet): info = dict(x={}, y={}) -class PermissionSetTest(unittest.TestCase): - def test_dumps(self): - self.assertEquals( - '+all', PermissionSet(all=True).dumps()) - self.assertEquals( - '+all', PermissionSet(all=True, other=True).dumps()) - self.assertEquals( - '+a,-b', PermissionSet(a=True, b=False).dumps()) - - def test_loads(self): - self.assertEquals("", TestPermissionSet.loads(None).dumps()) - self.assertEquals("", TestPermissionSet.loads("").dumps()) - self.assertEquals("+x,+y", TestPermissionSet.loads("+x,+y").dumps()) - self.assertEquals("+x,-y", TestPermissionSet.loads("+x,-y").dumps()) - self.assertEquals("+all", TestPermissionSet.loads("+x,-y,+all").dumps()) - self.assertEquals("+x,-y,+z", - TestPermissionSet.loads("+x,-y,+z").dumps()) - self.assertRaises(ValueError, - TestPermissionSet.loads, "+x,-y,+z", validate=True) - self.assertEquals( - "+x,-y", - TestPermissionSet.loads("-all,+x,-y", validate=True).dumps()) - - def test_is_superuser(self): - perm_set = PermissionSet() - self.assertFalse(perm_set.is_superuser()) - perm_set[perm_set.ALL] = True - self.assertTrue(perm_set.is_superuser()) - perm_set[perm_set.ALL] = False - self.assertFalse(perm_set.is_superuser()) - - def test_is_valid(self): - perm_set = PermissionSet() - self.assertFalse(perm_set.is_valid()) - - perm_set = TestPermissionSet() - self.assertTrue(perm_set.is_valid()) - perm_set['x'] = True - self.assertTrue(perm_set.is_valid()) - perm_set[perm_set.ALL] = True - self.assertTrue(perm_set.is_valid()) - perm_set['z'] = True - self.assertFalse(perm_set.is_valid()) - - def test_getitem(self): - perm_set = PermissionSet() - perm_set[perm_set.ALL] = True - self.assertFalse(perm_set['x']) - - perm_set = TestPermissionSet() - perm_set['x'] = True - self.assertTrue(perm_set['x']) - self.assertFalse(perm_set['y']) - perm_set['x'] = False - self.assertFalse(perm_set['x']) - perm_set[perm_set.ALL] = True - self.assertTrue(perm_set['x']) - self.assertTrue(perm_set['y']) - self.assertFalse(perm_set['z']) - self.assertTrue(perm_set.get('x', False)) - self.assertFalse(perm_set.get('z', False)) - self.assertTrue(perm_set.get('z', True)) - - -class ModeratorPermissionSetTest(unittest.TestCase): - def test_loads(self): - self.assertTrue(ModeratorPermissionSet.loads(None).is_superuser()) - self.assertFalse(ModeratorPermissionSet.loads('').is_superuser()) - class SRMemberTest(unittest.TestCase): def setUp(self):