mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-04-05 03:00:15 -04:00
Moderation log on ModSR and MultiReddit.
This commit is contained in:
committed by
Neil Williams
parent
4157f4e10b
commit
6f3c20a96d
@@ -21,7 +21,7 @@
|
||||
################################################################################
|
||||
from validator import *
|
||||
from pylons.i18n import _, ungettext
|
||||
from reddit_base import RedditController, base_listing, base_cassandra_listing
|
||||
from reddit_base import RedditController, base_listing, paginated_listing
|
||||
from r2 import config
|
||||
from r2.models import *
|
||||
from r2.lib.pages import *
|
||||
@@ -36,6 +36,7 @@ from r2.lib.filters import unsafe
|
||||
from r2.lib.emailer import has_opted_out, Email
|
||||
from r2.lib.db.operators import desc
|
||||
from r2.lib.db import queries
|
||||
from r2.lib.db.tdb_cassandra import MultiColumnQuery
|
||||
from r2.lib.strings import strings
|
||||
from r2.lib.solrsearch import RelatedSearchQuery, SubredditSearchQuery
|
||||
from r2.lib.indextank import IndextankQuery, IndextankException, InvalidIndextankQuery
|
||||
@@ -362,16 +363,14 @@ class FrontController(RedditController):
|
||||
else:
|
||||
return self.abort404()
|
||||
|
||||
def _make_moderationlog(self, num, after, reverse, count, mod=None, action=None):
|
||||
def _make_moderationlog(self, srs, num, after, reverse, count, mod=None, action=None):
|
||||
|
||||
if mod and action:
|
||||
query = c.site.get_modactions(mod=mod, action=None)
|
||||
|
||||
query = Subreddit.get_modactions(srs, mod=mod, action=None)
|
||||
def keep_fn(ma):
|
||||
return ma.action == action
|
||||
else:
|
||||
query = c.site.get_modactions(mod=mod, action=action)
|
||||
|
||||
query = Subreddit.get_modactions(srs, mod=mod, action=action)
|
||||
def keep_fn(ma):
|
||||
return True
|
||||
|
||||
@@ -383,27 +382,47 @@ class FrontController(RedditController):
|
||||
pane = listing.listing()
|
||||
return pane
|
||||
|
||||
@base_cassandra_listing
|
||||
@paginated_listing(max_page_size=500, backend='cassandra')
|
||||
@validate(mod=VAccountByName('mod'),
|
||||
action=VOneOf('type', ModAction.actions))
|
||||
def GET_moderationlog(self, num, after, reverse, count, mod, action):
|
||||
|
||||
is_moderator = c.user_is_loggedin and c.site.is_moderator(c.user) or c.user_is_admin
|
||||
|
||||
if not is_moderator:
|
||||
if not c.user_is_loggedin:
|
||||
return self.abort404()
|
||||
|
||||
if isinstance(c.site, ModSR) or isinstance(c.site, MultiReddit):
|
||||
if isinstance(c.site, ModSR):
|
||||
srs = Subreddit._byID(c.site.sr_ids(), return_dict=False)
|
||||
else:
|
||||
srs = Subreddit._byID(c.site.sr_ids, return_dict=False)
|
||||
|
||||
# check that user is mod on all requested srs
|
||||
if not Subreddit.user_mods_all(c.user, srs) and not c.user_is_admin:
|
||||
return self.abort404()
|
||||
|
||||
# grab all moderators
|
||||
mod_ids = set(Subreddit.get_all_mod_ids(srs))
|
||||
mods = Account._byID(mod_ids, data=True)
|
||||
|
||||
pane = self._make_moderationlog(srs, num, after, reverse, count,
|
||||
mod=mod, action=action)
|
||||
elif isinstance(c.site, FakeSubreddit):
|
||||
return self.abort404()
|
||||
else:
|
||||
if not c.site.is_moderator(c.user) and not c.user_is_admin:
|
||||
return self.abort404()
|
||||
mod_ids = c.site.moderators
|
||||
mods = Account._byID(mod_ids, data=True)
|
||||
|
||||
pane = self._make_moderationlog(c.site, num, after, reverse, count,
|
||||
mod=mod, action=action)
|
||||
|
||||
panes = PaneStack()
|
||||
pane = self._make_moderationlog(num, after, reverse, count,
|
||||
mod=mod, action=action)
|
||||
panes.append(pane)
|
||||
|
||||
action_buttons = [NavButton(_('all'), None, opt='type', css_class='primary')]
|
||||
for a in ModAction.actions:
|
||||
action_buttons.append(NavButton(ModAction._menu[a], a, opt='type'))
|
||||
|
||||
mod_ids = c.site.moderators
|
||||
mods = Account._byID(mod_ids)
|
||||
|
||||
mod_buttons = [NavButton(_('all'), None, opt='mod', css_class='primary')]
|
||||
for mod_id in mod_ids:
|
||||
mod = mods[mod_id]
|
||||
@@ -413,7 +432,8 @@ class FrontController(RedditController):
|
||||
title=_('filter by action'), type='lightdrop', css_class='modaction-drop'),
|
||||
NavMenu(mod_buttons, base_path=base_path,
|
||||
title=_('filter by moderator'), type='lightdrop')]
|
||||
return EditReddit(content=panes, nav_menus=menus, extension_handling=False).render()
|
||||
return EditReddit(content=panes, nav_menus=menus,
|
||||
extension_handling=False).render()
|
||||
|
||||
def _make_spamlisting(self, location, num, after, reverse, count):
|
||||
if location == 'reports':
|
||||
|
||||
@@ -468,9 +468,6 @@ def paginated_listing(default_page_size=25, max_page_size=100, backend='sql'):
|
||||
def base_listing(fn):
|
||||
return paginated_listing()(fn)
|
||||
|
||||
def base_cassandra_listing(fn):
|
||||
return paginated_listing(backend='cassandra')(fn)
|
||||
|
||||
def is_trusted_origin(origin):
|
||||
try:
|
||||
origin = urlparse(origin)
|
||||
|
||||
@@ -147,7 +147,7 @@ class ModAction(tdb_cassandra.UuidThing, Printable):
|
||||
v.add_object(self)
|
||||
|
||||
@classmethod
|
||||
def get_actions(cls, sr, mod=None, action=None, after=None, reverse=False, count=1000):
|
||||
def get_actions(cls, srs, mod=None, action=None, after=None, reverse=False, count=1000):
|
||||
"""
|
||||
Get a ColumnQuery that yields ModAction objects according to
|
||||
specified criteria.
|
||||
@@ -160,15 +160,17 @@ class ModAction(tdb_cassandra.UuidThing, Printable):
|
||||
if not isinstance(after, cls):
|
||||
after = None
|
||||
|
||||
srs = tup(srs)
|
||||
|
||||
if not mod and not action:
|
||||
rowkey = sr._id36
|
||||
q = ModActionBySR.query(rowkey, after=after, reverse=reverse, count=count)
|
||||
rowkeys = [sr._id36 for sr in srs]
|
||||
q = ModActionBySR.query(rowkeys, after=after, reverse=reverse, count=count)
|
||||
elif mod and not action:
|
||||
rowkey = '%s_%s' % (sr._id36, mod._id36)
|
||||
q = ModActionBySRMod.query(rowkey, after=after, reverse=reverse, count=count)
|
||||
rowkeys = ['%s_%s' % (sr._id36, mod._id36) for sr in srs]
|
||||
q = ModActionBySRMod.query(rowkeys, after=after, reverse=reverse, count=count)
|
||||
elif not mod and action:
|
||||
rowkey = '%s_%s' % (sr._id36, action)
|
||||
q = ModActionBySRAction.query(rowkey, after=after, reverse=reverse, count=count)
|
||||
rowkeys = ['%s_%s' % (sr._id36, action) for sr in srs]
|
||||
q = ModActionBySRAction.query(rowkeys, after=after, reverse=reverse, count=count)
|
||||
else:
|
||||
raise NotImplementedError("Can't query by both mod and action")
|
||||
|
||||
@@ -182,6 +184,13 @@ class ModAction(tdb_cassandra.UuidThing, Printable):
|
||||
text += ' %s' % self.description
|
||||
return text
|
||||
|
||||
@staticmethod
|
||||
def get_rgb(i, fade=0.8):
|
||||
r = int(256 - (hash(str(i)) % 256)*(1-fade))
|
||||
g = int(256 - (hash(str(i) + ' ') % 256)*(1-fade))
|
||||
b = int(256 - (hash(str(i) + ' ') % 256)*(1-fade))
|
||||
return (r, g, b)
|
||||
|
||||
@classmethod
|
||||
def add_props(cls, user, wrapped):
|
||||
|
||||
@@ -198,7 +207,7 @@ class ModAction(tdb_cassandra.UuidThing, Printable):
|
||||
targets = Thing._by_fullname(target_fullnames, data=True)
|
||||
authors = Account._byID([t.author_id for t in targets.values() if hasattr(t, 'author_id')], data=True)
|
||||
links = Link._byID([t.link_id for t in targets.values() if hasattr(t, 'link_id')], data=True)
|
||||
subreddits = Subreddit._byID([t.sr_id for t in targets.values() if hasattr(t, 'sr_id')])
|
||||
subreddits = Subreddit._byID([item.sr_id for item in wrapped], data=True)
|
||||
|
||||
# Assemble target links
|
||||
target_links = {}
|
||||
@@ -228,7 +237,7 @@ class ModAction(tdb_cassandra.UuidThing, Printable):
|
||||
'author': author.name,
|
||||
'on': _('on'),
|
||||
'title': short_title}
|
||||
path = target.make_permalink(link, subreddits[target.sr_id])
|
||||
path = target.make_permalink(link, subreddits[link.sr_id])
|
||||
target_links[fullname] = (text, path, title)
|
||||
elif isinstance(target, Account):
|
||||
target_accounts[fullname] = WrappedUser(target)
|
||||
@@ -252,6 +261,10 @@ class ModAction(tdb_cassandra.UuidThing, Printable):
|
||||
elif isinstance(target, Link) or isinstance(target, Comment):
|
||||
item.target_text, item.target_path, item.target_title = target_links[item.target_fullname]
|
||||
|
||||
item.bgcolor = ModAction.get_rgb(item.sr_id)
|
||||
item.sr_name = subreddits[item.sr_id].name
|
||||
item.sr_path = subreddits[item.sr_id].path
|
||||
|
||||
Printable.add_props(user, wrapped)
|
||||
|
||||
class ModActionBySR(tdb_cassandra.View):
|
||||
|
||||
@@ -332,10 +332,11 @@ class Subreddit(Thing, Printable):
|
||||
from r2.lib.db import queries
|
||||
return queries.get_sr_comments(self)
|
||||
|
||||
def get_modactions(self, mod=None, action=None):
|
||||
@classmethod
|
||||
def get_modactions(cls, srs, mod=None, action=None):
|
||||
# Get a query that will yield ModAction objects with mod and action
|
||||
from r2.models import ModAction
|
||||
return ModAction.get_actions(self, mod=mod, action=action)
|
||||
return ModAction.get_actions(srs, mod=mod, action=action)
|
||||
|
||||
@classmethod
|
||||
def add_props(cls, user, wrapped):
|
||||
@@ -619,6 +620,28 @@ class Subreddit(Thing, Printable):
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
@staticmethod
|
||||
def user_mods_all(user, srs):
|
||||
# Get moderator SRMember relations for all in srs
|
||||
# if a relation doesn't exist there will be a None entry in the
|
||||
# returned dict
|
||||
mod_rels = SRMember._fast_query(srs, user, 'moderator', data=False)
|
||||
if None in mod_rels.values():
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def get_all_mod_ids(srs):
|
||||
from r2.lib.db.thing import Merge
|
||||
srs = tup(srs)
|
||||
queries = [SRMember._query(SRMember.c._thing1_id == sr._id,
|
||||
SRMember.c._name == 'moderator') for sr in srs]
|
||||
merged = Merge(queries)
|
||||
# sr_ids = [sr._id for sr in srs]
|
||||
# query = SRMember._query(SRMember.c._thing1_id == sr_ids, ...)
|
||||
# is really slow
|
||||
return [rel._thing2_id for rel in list(merged)]
|
||||
|
||||
class FakeSubreddit(Subreddit):
|
||||
over_18 = False
|
||||
|
||||
@@ -4995,6 +4995,12 @@ tr.gold-accent + tr > td {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.modactionlisting td.subreddit {
|
||||
white-space: nowrap;
|
||||
padding-left: 0;
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
|
||||
.modactions td {
|
||||
font-size: small;
|
||||
text-align: left;
|
||||
|
||||
@@ -22,15 +22,27 @@
|
||||
|
||||
<%namespace file="utils.html" import="timestamp, plain_link"/>
|
||||
|
||||
<tr class="modactions">
|
||||
<%
|
||||
from pylons import c
|
||||
from r2.models.subreddit import ModSR, MultiReddit
|
||||
bgcolor = "rgb(255,255,255)"
|
||||
is_multi = isinstance(c.site, ModSR) or isinstance(c.site, MultiReddit)
|
||||
if is_multi and hasattr(thing, 'bgcolor'):
|
||||
bgcolor = 'rgb(%s,%s,%s)' % thing.bgcolor
|
||||
%>
|
||||
|
||||
<tr class="modactions" style="background-color: ${bgcolor}">
|
||||
<td class="timestamp whitespace:nowrap">${timestamp(thing.date)} ago</td>
|
||||
%if is_multi:
|
||||
<td class="subreddit">${plain_link('/r/' + thing.sr_name, thing.sr_path + 'about/log', title=thing.sr_name)}</td>
|
||||
%endif
|
||||
<td class="whitespace:nowrap">${thing.mod}</td>
|
||||
<td class="button">${thing.button}</td>
|
||||
<td class="description">${thing.text} 
|
||||
%if hasattr(thing, 'target_text'):
|
||||
${plain_link(thing.target_text, thing.target_path, title=thing.target_title, sr_path=False, cname=False, _class="subreddit hover")}
|
||||
${plain_link(thing.target_text, thing.target_path, title=thing.target_title, sr_path=False, cname=False, _class="subreddit hover")} 
|
||||
%elif hasattr(thing, 'target_wrapped_user'):
|
||||
${thing.target_wrapped_user}
|
||||
${thing.target_wrapped_user} 
|
||||
%endif
|
||||
%if hasattr(thing, 'details') and thing.details:
|
||||
<em>(${thing.details})</em>
|
||||
|
||||
Reference in New Issue
Block a user