def message_notification_email(data): """Queues a system email for a new message notification.""" from v1.lib.pages import MessageNotificationEmail MAX_EMAILS_PER_DAY = 1000 MESSAGE_THROTTLE_KEY = 'message_notification_emails' # If our counter's expired, initialize it again. g.cache.add(MESSAGE_THROTTLE_KEY, 0, time=24 * 60 * 60) for datum in data.itervalues(): datum = json.loads(datum) user = Account._byID36(datum['to'], data=True) comment = Comment._by_fullname(datum['comment'], data=True) # In case a user has enabled the preference while it was enabled for # them, but we've since turned it off. We need to explicitly state the # user because we're not in the context of an HTTP request from them. if not feature.is_enabled('orangereds_as_emails', user=user): continue if g.cache.get(MESSAGE_THROTTLE_KEY) > MAX_EMAILS_PER_DAY: raise Exception( 'Message notification emails: safety limit exceeded!') mac = generate_notification_email_unsubscribe_token( datum['to'], user_email=user.email, user_password_hash=user.password) base = g.https_endpoint or g.origin unsubscribe_link = base + '/mail/unsubscribe/%s/%s' % (datum['to'], mac) templateData = { 'sender_username': datum.get('from', ''), 'comment': comment, 'permalink': datum['permalink'], 'unsubscribe_link': unsubscribe_link, } _system_email( user.email, MessageNotificationEmail(**templateData).render(style='email'), Email.Kind.MESSAGE_NOTIFICATION, from_address=g.notification_email) g.stats.simple_event('email.message_notification.queued') g.cache.incr(MESSAGE_THROTTLE_KEY)
def get_details(cls, thing, voters=None): from v1.models import Comment, Link if isinstance(thing, Link): details_cls = VoteDetailsByLink elif isinstance(thing, Comment): details_cls = VoteDetailsByComment else: raise ValueError voter_id36s = None if voters: voter_id36s = [voter._id36 for voter in voters] try: row = details_cls._byID(thing._id36, properties=voter_id36s) raw_details = row._values() except tdb_cassandra.NotFound: return [] try: row = VoterIPByThing._byID(thing._fullname, properties=voter_id36s) ips = row._values() except tdb_cassandra.NotFound: ips = {} details = [] for voter_id36, json_data in raw_details.iteritems(): data = json.loads(json_data) data = cls.convert_old_details(data) user = Account._byID36(voter_id36, data=True) direction = Vote.deserialize_direction(data.pop("direction")) date = datetime.utcfromtimestamp(data.pop("date")) effects = data.pop("effects") data["ip"] = ips.get(voter_id36) vote = Vote(user, thing, direction, date, data, effects, get_previous_vote=False) details.append(vote) details.sort(key=lambda d: d.date) return details
def generate_notification_email_unsubscribe_token(user_id36, user_email=None, user_password_hash=None): """Generate a token used for one-click unsubscribe links for notification emails. user_id36: A base36-encoded user id. user_email: The user's email. Looked up if not provided. user_password_hash: The hash of the user's password. Looked up if not provided. """ import hashlib import hmac if (not user_email) or (not user_password_hash): user = Account._byID36(user_id36, data=True) if not user_email: user_email = user.email if not user_password_hash: user_password_hash = user.password return hmac.new(g.secrets['email_notifications'], user_id36 + user_email + user_password_hash, hashlib.sha256).hexdigest()
def add_props(cls, user, wrapped): from v1.lib.db.thing import Thing from v1.lib.menus import QueryButton from v1.lib.pages import WrappedUser from v1.models import ( Account, Link, ModSR, MultiVerbify, Subverbify, ) target_names = {item.target_fullname for item in wrapped if hasattr(item, "target_fullname")} targets = Thing._by_fullname(target_names, data=True) # get moderators moderators = Account._byID36({item.mod_id36 for item in wrapped}, data=True) # get authors for targets that are Links or Comments target_author_names = {target.author_id for target in targets.values() if hasattr(target, "author_id")} target_authors = Account._byID(target_author_names, data=True) # get parent links for targets that are Comments parent_link_names = {target.link_id for target in targets.values() if hasattr(target, "link_id")} parent_links = Link._byID(parent_link_names, data=True) # get subverbifys srs = Subverbify._byID36({item.sr_id36 for item in wrapped}, data=True) for item in wrapped: item.moderator = moderators[item.mod_id36] item.subverbify = srs[item.sr_id36] item.text = cls._text.get(item.action, '') item.target = None item.target_author = None if hasattr(item, "target_fullname") and item.target_fullname: item.target = targets[item.target_fullname] if hasattr(item.target, "author_id"): author_name = item.target.author_id item.target_author = target_authors[author_name] if hasattr(item.target, "link_id"): parent_link_name = item.target.link_id item.parent_link = parent_links[parent_link_name] if isinstance(item.target, Account): item.target_author = item.target if c.render_style == "html": request_path = request.path # make wrapped users for targets that are accounts user_targets = filter(lambda target: isinstance(target, Account), targets.values()) wrapped_user_targets = {user._fullname: WrappedUser(user) for user in user_targets} for item in wrapped: if isinstance(item.target, Account): user_name = item.target._fullname item.wrapped_user_target = wrapped_user_targets[user_name] css_class = 'modactions %s' % item.action action_button = QueryButton( '', item.action, query_param='type', css_class=css_class) action_button.build(base_path=request_path) item.action_button = action_button mod_button = QueryButton( item.moderator.name, item.moderator.name, query_param='mod') mod_button.build(base_path=request_path) item.mod_button = mod_button if isinstance(c.site, ModSR) or isinstance(c.site, MultiVerbify): rgb = item.subverbify.get_rgb() item.bgcolor = 'rgb(%s,%s,%s)' % rgb item.is_multi = True else: item.bgcolor = "rgb(255,255,255)" item.is_multi = False