def insert(userid, target_user, parentid, content, staffnotes): # Check invalid content if not content: raise WeasylError("commentInvalid") elif not target_user or not d.is_vouched_for(target_user): raise WeasylError("Unexpected") # Determine parent userid if parentid: parentuserid = d.engine.scalar( "SELECT userid FROM comments WHERE commentid = %(parent)s", parent=parentid, ) if parentuserid is None: raise WeasylError("shoutRecordMissing") else: parentuserid = None # Check permissions if userid not in staff.MODS: if ignoreuser.check(target_user, userid): raise WeasylError("pageOwnerIgnoredYou") elif ignoreuser.check(userid, target_user): raise WeasylError("youIgnoredPageOwner") elif ignoreuser.check(parentuserid, userid): raise WeasylError("replyRecipientIgnoredYou") elif ignoreuser.check(userid, parentuserid): raise WeasylError("youIgnoredReplyRecipient") _, is_banned, _ = d.get_login_settings(target_user) profile_config = d.get_config(target_user) if is_banned or "w" in profile_config or "x" in profile_config and not frienduser.check( userid, target_user): raise WeasylError("insufficientActionPermissions") # Create comment settings = 's' if staffnotes else '' co = d.meta.tables['comments'] db = d.connect() commentid = db.scalar(co.insert().values(userid=userid, target_user=target_user, parentid=parentid or None, content=content, unixtime=arrow.utcnow(), settings=settings).returning( co.c.commentid)) # Create notification if parentid and userid != parentuserid: if not staffnotes or parentuserid in staff.MODS: welcome.shoutreply_insert(userid, commentid, parentuserid, parentid, staffnotes) elif not staffnotes and target_user and userid != target_user: welcome.shout_insert(userid, commentid, otherid=target_user) d.metric('increment', 'shouts') return commentid
def offer(userid, submitid, otherid): query = d.engine.execute( "SELECT userid, rating, settings FROM submission WHERE submitid = %(id)s", id=submitid, ).first() if not query or "h" in query[2]: raise WeasylError("Unexpected") elif userid != query[0]: raise WeasylError("Unexpected") # Check collection acceptability if otherid: rating = d.get_rating(otherid) if rating < query[1]: raise WeasylError("collectionUnacceptable") if "f" in query[2]: raise WeasylError("collectionUnacceptable") if ignoreuser.check(otherid, userid): raise WeasylError("IgnoredYou") if ignoreuser.check(userid, otherid): raise WeasylError("YouIgnored") if blocktag.check(otherid, submitid=submitid): raise WeasylError("collectionUnacceptable") try: d.execute( "INSERT INTO collection (userid, submitid, unixtime) VALUES (%i, %i, %i)", [otherid, submitid, d.get_time()]) except PostgresError: raise WeasylError("collectionExists") welcome.collectoffer_insert(userid, otherid, submitid)
def insert(userid, shout, staffnotes=False): # Check invalid content if not shout.content: raise WeasylError("commentInvalid") elif not shout.userid: raise WeasylError("Unexpected") # Determine indent and parentuserid if shout.parentid: query = d.execute("SELECT userid, indent FROM comments WHERE commentid = %i", [shout.parentid], options="single") if not query: raise WeasylError("shoutRecordMissing") indent, parentuserid = query[1] + 1, query[0] else: indent, parentuserid = 0, None # Check permissions if userid not in staff.MODS: if ignoreuser.check(shout.userid, userid): raise WeasylError("pageOwnerIgnoredYou") elif ignoreuser.check(userid, shout.userid): raise WeasylError("youIgnoredPageOwner") elif ignoreuser.check(parentuserid, userid): raise WeasylError("replyRecipientIgnoredYou") elif ignoreuser.check(userid, parentuserid): raise WeasylError("youIgnoredReplyRecipient") settings = d.execute("SELECT lo.settings, pr.config FROM login lo" " INNER JOIN profile pr ON lo.userid = pr.userid" " WHERE lo.userid = %i", [shout.userid], options="single") if "b" in settings[0] or "w" in settings[1] or "x" in settings[1] and not frienduser.check(userid, shout.userid): raise WeasylError("insufficientActionPermissions") # Create comment settings = 's' if staffnotes else '' co = d.meta.tables['comments'] db = d.connect() commentid = db.scalar( co.insert() .values(userid=userid, target_user=shout.userid, parentid=shout.parentid or None, content=shout.content, unixtime=arrow.utcnow(), indent=indent, settings=settings) .returning(co.c.commentid)) # Create notification if shout.parentid and userid != parentuserid: if not staffnotes or parentuserid in staff.MODS: welcome.shoutreply_insert(userid, commentid, parentuserid, shout.parentid, staffnotes) elif not staffnotes and shout.userid and userid != shout.userid: welcome.shout_insert(userid, commentid, otherid=shout.userid) d.metric('increment', 'shouts') return commentid
def insert(userid, submitid=None, charid=None, journalid=None): if submitid: content_table, id_field, target = "submission", "submitid", submitid elif charid: content_table, id_field, target = "character", "charid", charid else: content_table, id_field, target = "journal", "journalid", journalid query = d.execute("SELECT userid, settings FROM %s WHERE %s = %i", [content_table, id_field, target], options="single") if not query: raise WeasylError("TargetRecordMissing") elif userid == query[0]: raise WeasylError("CannotSelfFavorite") elif "f" in query[1] and not frienduser.check(userid, query[0]): raise WeasylError("FriendsOnly") elif ignoreuser.check(userid, query[0]): raise WeasylError("YouIgnored") elif ignoreuser.check(query[0], userid): raise WeasylError("contentOwnerIgnoredYou") insert_result = d.engine.execute( 'INSERT INTO favorite (userid, targetid, type, unixtime) ' 'VALUES (%(user)s, %(target)s, %(type)s, %(now)s) ' 'ON CONFLICT DO NOTHING', user=userid, target=d.get_targetid(submitid, charid, journalid), type='s' if submitid else 'f' if charid else 'j', now=d.get_time()) if insert_result.rowcount == 0: return # create a list of users to notify notified = set(collection.find_owners(submitid)) # conditions under which "other" should be notified def can_notify(other): other_jsonb = d.get_profile_settings(other) allow_notify = other_jsonb.allow_collection_notifs not_ignored = not ignoreuser.check(other, userid) return allow_notify and not_ignored notified = set(filter(can_notify, notified)) # always notify for own content notified.add(query[0]) for other in notified: welcome.favorite_insert(userid, submitid=submitid, charid=charid, journalid=journalid, otherid=other)
def insert(userid, otherid): if ignoreuser.check(otherid, userid): raise WeasylError("IgnoredYou") elif ignoreuser.check(userid, otherid): raise WeasylError("YouIgnored") d.engine.execute( 'INSERT INTO watchuser (userid, otherid, settings) VALUES (%(user)s, %(other)s, %(settings)s) ' 'ON CONFLICT DO NOTHING', user=userid, other=otherid, settings=WatchSettings.from_code(d.get_config(userid)).to_code()) from weasyl import welcome welcome.followuser_remove(userid, otherid) welcome.followuser_insert(userid, otherid)
def request(userid, otherid): if ignoreuser.check(otherid, userid): raise WeasylError("IgnoredYou") elif ignoreuser.check(userid, otherid): raise WeasylError("YouIgnored") if already_pending(otherid, userid): d.execute("UPDATE frienduser SET settings = REPLACE(settings, 'p', '') WHERE (userid, otherid) = (%i, %i)", [otherid, userid]) welcome.frienduseraccept_insert(userid, otherid) welcome.frienduserrequest_remove(userid, otherid) elif not already_pending(userid, otherid): d.execute("INSERT INTO frienduser VALUES (%i, %i)", [userid, otherid]) welcome.frienduserrequest_remove(userid, otherid) welcome.frienduserrequest_insert(userid, otherid)
def request(userid, submitid, otherid): query = d.engine.execute( "SELECT userid, rating, settings " "FROM submission WHERE submitid = %(submission)s", submission=submitid).first() rating = d.get_rating(userid) if not query or "h" in query.settings: raise WeasylError("Unexpected") if otherid != query.userid: raise WeasylError("Unexpected") # not checking for blocktags here because if you want to collect # something with a tag you don't like that's your business if rating < query.rating: raise WeasylError("RatingExceeded") if "f" in query.settings: raise WeasylError("collectionUnacceptable") if ignoreuser.check(otherid, userid): raise WeasylError("IgnoredYou") if ignoreuser.check(userid, otherid): raise WeasylError("YouIgnored") if _check_throttle(userid, otherid): raise WeasylError("collectionThrottle") settings = d.get_profile_settings(otherid) if not settings.allow_collection_requests: raise WeasylError("Unexpected") request_settings = "r" try: d.engine.execute( "INSERT INTO collection (userid, submitid, unixtime, settings) " "VALUES (%(userid)s, %(submitid)s, %(now)s, %(settings)s)", userid=userid, submitid=submitid, now=d.get_time(), settings=request_settings) except PostgresError: raise WeasylError("collectionExists") welcome.collectrequest_insert(userid, otherid, submitid)
def request(userid, otherid): if ignoreuser.check(otherid, userid): raise WeasylError("IgnoredYou") elif ignoreuser.check(userid, otherid): raise WeasylError("YouIgnored") if d.execute("SELECT EXISTS (SELECT 0 FROM frienduser WHERE (userid, otherid) = (%i, %i) AND settings ~ 'p')", [otherid, userid], options="bool"): d.execute("UPDATE frienduser SET settings = REPLACE(settings, 'p', '') WHERE (userid, otherid) = (%i, %i)", [otherid, userid]) welcome.frienduseraccept_insert(userid, otherid) welcome.frienduserrequest_remove(userid, otherid) elif not d.execute("SELECT EXISTS (SELECT 0 FROM frienduser WHERE (userid, otherid) = (%i, %i) AND settings ~ 'p')", [userid, otherid], options="bool"): d.execute("INSERT INTO frienduser VALUES (%i, %i)", [userid, otherid]) welcome.frienduserrequest_remove(userid, otherid) welcome.frienduserrequest_insert(userid, otherid)
def POST(self): form = web.input(userid="") otherid = define.get_int(form.userid) if form.action == "ignore": if not ignoreuser.check(self.user_id, otherid): ignoreuser.insert(self.user_id, otherid) elif form.action == "unignore": ignoreuser.remove(self.user_id, otherid) raise web.seeother("/~%s" % (define.get_sysname(define.get_display_name(otherid))))
def ignoreuser_(request): form = request.web_input(userid="") otherid = define.get_int(form.userid) if form.action == "ignore": if not ignoreuser.check(request.userid, otherid): ignoreuser.insert(request.userid, otherid) elif form.action == "unignore": ignoreuser.remove(request.userid, otherid) raise HTTPSeeOther(location="/~%s" % (define.get_sysname(define.get_display_name(otherid))))
def select_view_api(userid, submitid, anyway=False, increment_views=False): rating = d.get_rating(userid) db = d.connect() sub = db.query(orm.Submission).get(submitid) if sub is None or 'hidden' in sub.settings: raise WeasylError("submissionRecordMissing") sub_rating = sub.rating.code if 'friends-only' in sub.settings and not frienduser.check(userid, sub.userid): raise WeasylError("submissionRecordMissing") elif sub_rating > rating and userid != sub.userid: raise WeasylError("RatingExceeded") elif not anyway and ignoreuser.check(userid, sub.userid): raise WeasylError("UserIgnored") elif not anyway and blocktag.check(userid, submitid=submitid): raise WeasylError("TagBlocked") description = sub.content embedlink = None if 'embedded-content' in sub.settings: embedlink, _, description = description.partition('\n') elif 'gdocs-embed' in sub.settings: embedlink = sub.google_doc_embed.embed_url views = sub.page_views if increment_views and d.common_view_content(userid, submitid, 'submit'): views += 1 return { 'submitid': submitid, 'title': sub.title, 'owner': sub.owner.profile.username, 'owner_login': sub.owner.login_name, 'owner_media': api.tidy_all_media(media.get_user_media(sub.userid)), 'media': api.tidy_all_media(media.get_submission_media(submitid)), 'description': text.markdown(description), 'embedlink': embedlink, 'folderid': sub.folderid, 'folder_name': sub.folder.title if sub.folderid else None, 'posted_at': d.iso8601(sub.unixtime), 'tags': searchtag.select(submitid=submitid), 'link': d.absolutify_url("/submission/%d/%s" % (submitid, text.slug_for(sub.title))), 'type': 'submission', 'subtype': m.CATEGORY_PARSABLE_MAP[sub.subtype // 1000 * 1000], 'rating': sub.rating.name, 'views': views, 'favorites': favorite.count(submitid), 'comments': comment.count(submitid), 'favorited': favorite.check(userid, submitid=submitid), 'friends_only': 'friends-only' in sub.settings, }
def _select_character_and_check(userid, charid, rating=None, ignore=True, anyway=False, increment_views=True): """Selects a character, after checking if the user is authorized, etc. Args: userid (int): Currently authenticating user ID. charid (int): Character ID to fetch. rating (int): Maximum rating to display. Defaults to None. ignore (bool): Whether to respect ignored or blocked tags. Defaults to True. anyway (bool): Whether to ignore checks and display anyway. Defaults to False. increment_views (bool): Whether to increment the number of views on the submission. Defaults to True. Returns: A character and all needed data as a dict. """ query = define.engine.execute(""" SELECT ch.userid, pr.username, ch.unixtime, ch.char_name, ch.age, ch.gender, ch.height, ch.weight, ch.species, ch.content, ch.rating, ch.settings, ch.page_views, pr.config FROM character ch INNER JOIN profile pr USING (userid) WHERE ch.charid = %(charid)s """, charid=charid).first() if query and userid in staff.MODS and anyway: pass elif not query or 'h' in query.settings: raise WeasylError('characterRecordMissing') elif query.rating > rating and ( (userid != query.userid and userid not in staff.MODS) or define.is_sfw_mode()): raise WeasylError('RatingExceeded') elif 'f' in query.settings and not frienduser.check(userid, query.userid): raise WeasylError('FriendsOnly') elif ignore and ignoreuser.check(userid, query.userid): raise WeasylError('UserIgnored') elif ignore and blocktag.check(userid, charid=charid): raise WeasylError('TagBlocked') query = dict(query) if increment_views and define.common_view_content(userid, charid, 'char'): query['page_views'] += 1 return query
def _select_journal_and_check(userid, journalid, rating=None, ignore=True, anyway=False, increment_views=True): """Selects a journal, after checking if the user is authorized, etc. Args: userid (int): Currently authenticating user ID. journalid (int): Character ID to fetch. rating (int): Maximum rating to display. Defaults to None. ignore (bool): Whether to respect ignored or blocked tags. Defaults to True. anyway (bool): Whether ignore checks and display anyway. Defaults to False. increment_views (bool): Whether to increment the number of views on the submission. Defaults to True. Returns: A journal and all needed data as a dict. """ query = d.engine.execute(""" SELECT jo.userid, pr.username, jo.unixtime, jo.title, jo.content, jo.rating, jo.settings, jo.page_views, pr.config FROM journal jo JOIN profile pr ON jo.userid = pr.userid WHERE jo.journalid = %(id)s """, id=journalid).first() if not query: # If there's no query result, there's no record, so fast-fail. raise WeasylError('journalRecordMissing') elif journalid and userid in staff.MODS and anyway: pass elif not query or 'h' in query.settings: raise WeasylError('journalRecordMissing') elif query.rating > rating and ( (userid != query.userid and userid not in staff.MODS) or d.is_sfw_mode()): raise WeasylError('RatingExceeded') elif 'f' in query.settings and not frienduser.check(userid, query.userid): raise WeasylError('FriendsOnly') elif ignore and ignoreuser.check(userid, query.userid): raise WeasylError('UserIgnored') elif ignore and blocktag.check(userid, journalid=journalid): raise WeasylError('TagBlocked') query = dict(query) if increment_views and d.common_view_content(userid, journalid, 'journal'): query['page_views'] += 1 return query
def request(userid, otherid): if ignoreuser.check(otherid, userid): raise WeasylError("IgnoredYou") elif ignoreuser.check(userid, otherid): raise WeasylError("YouIgnored") if d.execute( "SELECT EXISTS (SELECT 0 FROM frienduser WHERE (userid, otherid) = (%i, %i) AND settings ~ 'p')", [otherid, userid], options="bool"): d.execute( "UPDATE frienduser SET settings = REPLACE(settings, 'p', '') WHERE (userid, otherid) = (%i, %i)", [otherid, userid]) welcome.frienduseraccept_insert(userid, otherid) welcome.frienduserrequest_remove(userid, otherid) elif not d.execute( "SELECT EXISTS (SELECT 0 FROM frienduser WHERE (userid, otherid) = (%i, %i) AND settings ~ 'p')", [userid, otherid], options="bool"): d.execute("INSERT INTO frienduser VALUES (%i, %i)", [userid, otherid]) welcome.frienduserrequest_remove(userid, otherid) welcome.frienduserrequest_insert(userid, otherid)
def _select_character_and_check(userid, charid, rating=None, ignore=True, anyway=False, increment_views=True): """Selects a character, after checking if the user is authorized, etc. Args: userid (int): Currently authenticating user ID. charid (int): Character ID to fetch. rating (int): Maximum rating to display. Defaults to None. ignore (bool): Whether to respect ignored or blocked tags. Defaults to True. anyway (bool): Whether to ignore checks and display anyway. Defaults to False. increment_views (bool): Whether to increment the number of views on the submission. Defaults to True. Returns: A character and all needed data as a dict. """ query = define.engine.execute(""" SELECT ch.userid, pr.username, ch.unixtime, ch.char_name, ch.age, ch.gender, ch.height, ch.weight, ch.species, ch.content, ch.rating, ch.settings, ch.page_views, pr.config FROM character ch INNER JOIN profile pr USING (userid) WHERE ch.charid = %(charid)s """, charid=charid).first() if query and userid in staff.MODS and anyway: pass elif not query or 'h' in query.settings: raise WeasylError('characterRecordMissing') elif query.rating > rating and ((userid != query.userid and userid not in staff.MODS) or define.is_sfw_mode()): raise WeasylError('RatingExceeded') elif 'f' in query.settings and not frienduser.check(userid, query.userid): raise WeasylError('FriendsOnly') elif ignore and ignoreuser.check(userid, query.userid): raise WeasylError('UserIgnored') elif ignore and blocktag.check(userid, charid=charid): raise WeasylError('TagBlocked') query = dict(query) if increment_views and define.common_view_content(userid, charid, 'char'): query['page_views'] += 1 return query
def _select_journal_and_check(userid, journalid, rating=None, ignore=True, anyway=False, increment_views=True): """Selects a journal, after checking if the user is authorized, etc. Args: userid (int): Currently authenticating user ID. journalid (int): Character ID to fetch. rating (int): Maximum rating to display. Defaults to None. ignore (bool): Whether to respect ignored or blocked tags. Defaults to True. anyway (bool): Whether ignore checks and display anyway. Defaults to False. increment_views (bool): Whether to increment the number of views on the submission. Defaults to True. Returns: A journal and all needed data as a dict. """ query = d.engine.execute(""" SELECT jo.userid, pr.username, jo.unixtime, jo.title, jo.content, jo.rating, jo.settings, jo.page_views, pr.config FROM journal jo JOIN profile pr ON jo.userid = pr.userid WHERE jo.journalid = %(id)s """, id=journalid).first() if journalid and userid in staff.MODS and anyway: pass elif not query or 'h' in query.settings: raise WeasylError('journalRecordMissing') elif query.rating > rating and ((userid != query.userid and userid not in staff.MODS) or d.is_sfw_mode()): raise WeasylError('RatingExceeded') elif 'f' in query.settings and not frienduser.check(userid, query.userid): raise WeasylError('FriendsOnly') elif ignore and ignoreuser.check(userid, query.userid): raise WeasylError('UserIgnored') elif ignore and blocktag.check(userid, journalid=journalid): raise WeasylError('TagBlocked') query = dict(query) if increment_views and d.common_view_content(userid, journalid, 'journal'): query['page_views'] += 1 return query
def associate(userid, tags, submitid=None, charid=None, journalid=None, preferred_tags_userid=None, optout_tags_userid=None): """ Associates searchtags with a content item. Parameters: userid: The userid of the user associating tags tags: A set of tags submitid: The ID number of a submission content item to associate ``tags`` to. (default: None) charid: The ID number of a character content item to associate ``tags`` to. (default: None) journalid: The ID number of a journal content item to associate ``tags`` to. (default: None) preferred_tags_userid: The ID number of a user to associate ``tags`` to for Preferred tags. (default: None) optout_tags_userid: The ID number of a user to associate ``tags`` to for Opt-Out tags. (default: None) Returns: A dict containing two elements. 1) ``add_failure_restricted_tags``, which contains a space separated string of tag titles which failed to be added to the content item due to the user or global restricted tag lists; and 2) ``remove_failure_owner_set_tags``, which contains a space separated string of tag titles which failed to be removed from the content item due to the owner of the aforementioned item prohibiting users from removing tags set by the content owner. If an element does not have tags, the element is set to None. If neither elements are set, the function returns None. """ targetid = d.get_targetid(submitid, charid, journalid) # Assign table, feature, ownerid if submitid: table, feature = "searchmapsubmit", "submit" ownerid = d.get_ownerid(submitid=targetid) elif charid: table, feature = "searchmapchar", "char" ownerid = d.get_ownerid(charid=targetid) elif journalid: table, feature = "searchmapjournal", "journal" ownerid = d.get_ownerid(journalid=targetid) elif preferred_tags_userid: table, feature = "artist_preferred_tags", "user" targetid = ownerid = preferred_tags_userid elif optout_tags_userid: table, feature = "artist_optout_tags", "user" targetid = ownerid = optout_tags_userid else: raise WeasylError("Unexpected") # Check permissions and invalid target if not ownerid: raise WeasylError("TargetRecordMissing") elif userid != ownerid and ("g" in d.get_config(userid) or preferred_tags_userid or optout_tags_userid): # disallow if user is forbidden from tagging, or trying to set artist tags on someone other than themselves raise WeasylError("InsufficientPermissions") elif ignoreuser.check(ownerid, userid): raise WeasylError("contentOwnerIgnoredYou") # Determine previous tagids, titles, and settings existing = d.engine.execute( "SELECT tagid, title, settings FROM {} INNER JOIN searchtag USING (tagid) WHERE targetid = %(target)s" .format(table), target=targetid).fetchall() # Retrieve tag titles and tagid pairs, for new (if any) and existing tags query = add_and_get_searchtags(tags) existing_tagids = {t.tagid for t in existing} entered_tagids = {t.tagid for t in query} # Assign added and removed added = entered_tagids - existing_tagids removed = existing_tagids - entered_tagids # enforce the limit on artist preference tags if preferred_tags_userid and (len(added) - len(removed) + len(existing)) > MAX_PREFERRED_TAGS: raise WeasylError("tooManyPreferenceTags") # Track which tags fail to be added or removed to later notify the user (Note: These are tagids at this stage) add_failure_restricted_tags = None remove_failure_owner_set_tags = None # If the modifying user is not the owner of the object, and is not staff, check user/global restriction lists if userid != ownerid and userid not in staff.MODS: user_rtags = set(query_user_restricted_tags(ownerid)) global_rtags = set(query_global_restricted_tags()) add_failure_restricted_tags = remove_restricted_tags( user_rtags | global_rtags, query) added -= add_failure_restricted_tags if len(add_failure_restricted_tags) == 0: add_failure_restricted_tags = None # Check removed artist tags if not can_remove_tags(userid, ownerid): existing_artist_tags = {t.tagid for t in existing if 'a' in t.settings} remove_failure_owner_set_tags = removed & existing_artist_tags removed.difference_update(existing_artist_tags) entered_tagids.update(existing_artist_tags) # Submission items use a different method of tag protection for artist tags; ignore them if submitid or len(remove_failure_owner_set_tags) == 0: remove_failure_owner_set_tags = None # Remove tags if removed: d.engine.execute( "DELETE FROM {} WHERE targetid = %(target)s AND tagid = ANY (%(removed)s)" .format(table), target=targetid, removed=list(removed)) if added: d.engine.execute( "INSERT INTO {} SELECT tag, %(target)s FROM UNNEST (%(added)s) AS tag" .format(table), target=targetid, added=list(added)) # preference/optout tags can only be set by the artist, so this settings column does not apply if userid == ownerid and not (preferred_tags_userid or optout_tags_userid): d.engine.execute( "UPDATE {} SET settings = settings || 'a' WHERE targetid = %(target)s AND tagid = ANY (%(added)s)" .format(table), target=targetid, added=list(added)) if submitid: d.engine.execute( 'INSERT INTO submission_tags (submitid, tags) VALUES (%(submission)s, %(tags)s) ' 'ON CONFLICT (submitid) DO UPDATE SET tags = %(tags)s', submission=submitid, tags=list(entered_tagids)) db = d.connect() db.execute(d.meta.tables['tag_updates'].insert().values( submitid=submitid, userid=userid, added=tag_array(added), removed=tag_array(removed))) if userid != ownerid: welcome.tag_update_insert(ownerid, submitid) files.append( "%stag.%s.%s.log" % (m.MACRO_SYS_LOG_PATH, feature, d.get_timestamp()), "-%sID %i -T %i -UID %i -X %s\n" % (feature[0].upper(), targetid, d.get_time(), userid, " ".join(tags))) # Return dict with any tag titles as a string that failed to be added or removed if add_failure_restricted_tags or remove_failure_owner_set_tags: if add_failure_restricted_tags: add_failure_restricted_tags = " ".join({ tag.title for tag in query if tag.tagid in add_failure_restricted_tags }) if remove_failure_owner_set_tags: remove_failure_owner_set_tags = " ".join({ tag.title for tag in existing if tag.tagid in remove_failure_owner_set_tags }) return { "add_failure_restricted_tags": add_failure_restricted_tags, "remove_failure_owner_set_tags": remove_failure_owner_set_tags } else: return None
def select_view(userid, submitid, rating, ignore=True, anyway=None): query = d.execute(""" SELECT su.userid, pr.username, su.folderid, su.unixtime, su.title, su.content, su.subtype, su.rating, su.settings, su.page_views, su.sorttime, pr.config, fd.title FROM submission su INNER JOIN profile pr USING (userid) LEFT JOIN folder fd USING (folderid) WHERE su.submitid = %i """, [submitid], options=["single", "list"]) # Sanity check if query and userid in staff.MODS and anyway == "true": pass elif not query or "h" in query[8]: raise WeasylError("submissionRecordMissing") elif query[7] > rating and ((userid != query[0] and userid not in staff.MODS) or d.is_sfw_mode()): raise WeasylError("RatingExceeded") elif "f" in query[8] and not frienduser.check(userid, query[0]): raise WeasylError("FriendsOnly") elif ignore and ignoreuser.check(userid, query[0]): raise WeasylError("UserIgnored") elif ignore and blocktag.check(userid, submitid=submitid): raise WeasylError("TagBlocked") # Get submission filename submitfile = media.get_submission_media(submitid).get('submission', [None])[0] # Get submission text if submitfile and submitfile['file_type'] in ['txt', 'htm']: submittext = files.read(submitfile['full_file_path']) else: submittext = None embedlink = d.text_first_line(query[5]) if "v" in query[8] else None google_doc_embed = None if 'D' in query[8]: db = d.connect() gde = d.meta.tables['google_doc_embeds'] q = (sa.select([gde.c.embed_url]) .where(gde.c.submitid == submitid)) results = db.execute(q).fetchall() if not results: raise WeasylError("can't find embed information") google_doc_embed = results[0] tags, artist_tags = searchtag.select_with_artist_tags(submitid) settings = d.get_profile_settings(query[0]) return { "submitid": submitid, "userid": query[0], "username": query[1], "folderid": query[2], "unixtime": query[3], "title": query[4], "content": (d.text_first_line(query[5], strip=True) if "v" in query[8] else query[5]), "subtype": query[6], "rating": query[7], "settings": query[8], "page_views": ( query[9] + 1 if d.common_view_content(userid, 0 if anyway == "true" else submitid, "submit") else query[9]), "fave_count": d.execute( "SELECT COUNT(*) FROM favorite WHERE (targetid, type) = (%i, 's')", [submitid], ["element"]), "mine": userid == query[0], "reported": report.check(submitid=submitid), "favorited": favorite.check(userid, submitid=submitid), "friends_only": "f" in query[8], "hidden_submission": "h" in query[8], "collectors": collection.find_owners(submitid), "no_request": not settings.allow_collection_requests, "text": submittext, "sub_media": media.get_submission_media(submitid), "user_media": media.get_user_media(query[0]), "submit": submitfile, "embedlink": embedlink, "embed": embed.html(embedlink) if embedlink is not None else None, "google_doc_embed": google_doc_embed, "tags": tags, "artist_tags": artist_tags, "removable_tags": searchtag.removable_tags(userid, query[0], tags, artist_tags), "can_remove_tags": searchtag.can_remove_tags(userid, query[0]), "folder_more": select_near(userid, rating, 1, query[0], query[2], submitid), "folder_title": query[12] if query[12] else "Root", "comments": comment.select(userid, submitid=submitid), }
def associate(userid, tags, submitid=None, charid=None, journalid=None): targetid = d.get_targetid(submitid, charid, journalid) # Assign table, feature, ownerid if submitid: table, feature = "searchmapsubmit", "submit" ownerid = d.get_ownerid(submitid=targetid) elif charid: table, feature = "searchmapchar", "char" ownerid = d.get_ownerid(charid=targetid) else: table, feature = "searchmapjournal", "journal" ownerid = d.get_ownerid(journalid=targetid) # Check permissions and invalid target if not ownerid: raise WeasylError("TargetRecordMissing") elif userid != ownerid and "g" in d.get_config(userid): raise WeasylError("InsufficientPermissions") elif ignoreuser.check(ownerid, userid): raise WeasylError("contentOwnerIgnoredYou") # Determine previous tags existing = d.engine.execute( "SELECT tagid, settings FROM {} WHERE targetid = %(target)s".format(table), target=targetid).fetchall() # Determine tag titles and tagids query = d.engine.execute( "SELECT tagid, title FROM searchtag WHERE title = ANY (%(tags)s)", tags=list(tags)).fetchall() newtags = list(tags - {x.title for x in query}) if newtags: query.extend( d.engine.execute( "INSERT INTO searchtag (title) SELECT * FROM UNNEST (%(newtags)s) AS title RETURNING tagid, title", newtags=newtags ).fetchall()) existing_tagids = {t.tagid for t in existing} entered_tagids = {t.tagid for t in query} # Assign added and removed added = entered_tagids - existing_tagids removed = existing_tagids - entered_tagids # Check removed artist tags if not can_remove_tags(userid, ownerid): existing_artist_tags = {t.tagid for t in existing if 'a' in t.settings} removed.difference_update(existing_artist_tags) entered_tagids.update(existing_artist_tags) # Remove tags if removed: d.engine.execute( "DELETE FROM {} WHERE targetid = %(target)s AND tagid = ANY (%(removed)s)".format(table), target=targetid, removed=list(removed)) if added: d.execute("INSERT INTO %s VALUES %s" % (table, d.sql_number_series([[i, targetid] for i in added]))) if userid == ownerid: d.execute( "UPDATE %s SET settings = settings || 'a' WHERE targetid = %i AND tagid IN %s", [table, targetid, d.sql_number_list(list(added))]) if submitid: try: d.engine.execute( 'INSERT INTO submission_tags (submitid, tags) VALUES (%(submission)s, %(tags)s)', submission=submitid, tags=list(entered_tagids)) except PostgresError: result = d.engine.execute( 'UPDATE submission_tags SET tags = %(tags)s WHERE submitid = %(submission)s', submission=submitid, tags=list(entered_tagids)) assert result.rowcount == 1 db = d.connect() db.execute( d.meta.tables['tag_updates'].insert() .values(submitid=submitid, userid=userid, added=tag_array(added), removed=tag_array(removed))) if userid != ownerid: welcome.tag_update_insert(ownerid, submitid) files.append( "%stag.%s.%s.log" % (m.MACRO_SYS_LOG_PATH, feature, d.get_timestamp()), "-%sID %i -T %i -UID %i -X %s\n" % (feature[0].upper(), targetid, d.get_time(), userid, " ".join(tags)))
def associate(userid, tags, submitid=None, charid=None, journalid=None): targetid = d.get_targetid(submitid, charid, journalid) # Assign table, feature, ownerid if submitid: table, feature = "searchmapsubmit", "submit" ownerid = d.get_ownerid(submitid=targetid) elif charid: table, feature = "searchmapchar", "char" ownerid = d.get_ownerid(charid=targetid) else: table, feature = "searchmapjournal", "journal" ownerid = d.get_ownerid(journalid=targetid) # Check permissions and invalid target if not ownerid: raise WeasylError("TargetRecordMissing") elif userid != ownerid and "g" in d.get_config(userid): raise WeasylError("InsufficientPermissions") elif ignoreuser.check(ownerid, userid): raise WeasylError("contentOwnerIgnoredYou") # Determine previous tags existing = d.engine.execute( "SELECT tagid, settings FROM {} WHERE targetid = %(target)s".format(table), target=targetid).fetchall() # Determine tag titles and tagids query = d.engine.execute( "SELECT tagid, title FROM searchtag WHERE title = ANY (%(tags)s)", tags=list(tags)).fetchall() newtags = list(tags - {x.title for x in query}) if newtags: query.extend( d.engine.execute( "INSERT INTO searchtag (title) SELECT * FROM UNNEST (%(newtags)s) AS title RETURNING tagid, title", newtags=newtags ).fetchall()) existing_tagids = {t.tagid for t in existing} entered_tagids = {t.tagid for t in query} # Assign added and removed added = entered_tagids - existing_tagids removed = existing_tagids - entered_tagids # Check removed artist tags if not can_remove_tags(userid, ownerid): existing_artist_tags = {t.tagid for t in existing if 'a' in t.settings} removed.difference_update(existing_artist_tags) entered_tagids.update(existing_artist_tags) # Remove tags if removed: d.engine.execute( "DELETE FROM {} WHERE targetid = %(target)s AND tagid = ANY (%(removed)s)".format(table), target=targetid, removed=list(removed)) if added: d.engine.execute( "INSERT INTO {} SELECT tag, %(target)s FROM UNNEST (%(added)s) AS tag".format(table), target=targetid, added=list(added)) if userid == ownerid: d.execute( "UPDATE %s SET settings = settings || 'a' WHERE targetid = %i AND tagid IN %s", [table, targetid, d.sql_number_list(list(added))]) if submitid: d.engine.execute( 'INSERT INTO submission_tags (submitid, tags) VALUES (%(submission)s, %(tags)s) ' 'ON CONFLICT (submitid) DO UPDATE SET tags = %(tags)s', submission=submitid, tags=list(entered_tagids)) db = d.connect() db.execute( d.meta.tables['tag_updates'].insert() .values(submitid=submitid, userid=userid, added=tag_array(added), removed=tag_array(removed))) if userid != ownerid: welcome.tag_update_insert(ownerid, submitid) files.append( "%stag.%s.%s.log" % (m.MACRO_SYS_LOG_PATH, feature, d.get_timestamp()), "-%sID %i -T %i -UID %i -X %s\n" % (feature[0].upper(), targetid, d.get_time(), userid, " ".join(tags)))
def select_view(userid, submitid, rating, ignore=True, anyway=None): query = d.engine.execute(""" SELECT su.userid, pr.username, su.folderid, su.unixtime, su.title, su.content, su.subtype, su.rating, su.settings, su.page_views, fd.title, su.favorites, su.image_representations FROM submission su INNER JOIN profile pr USING (userid) LEFT JOIN folder fd USING (folderid) WHERE su.submitid = %(id)s """, id=submitid).first() # Sanity check if query and userid in staff.MODS and anyway == "true": pass elif not query or "h" in query[8]: raise WeasylError("submissionRecordMissing") elif query[7] > rating and ( (userid != query[0] and userid not in staff.MODS) or d.is_sfw_mode()): raise WeasylError("RatingExceeded") elif "f" in query[8] and not frienduser.check(userid, query[0]): raise WeasylError("FriendsOnly") elif ignore and ignoreuser.check(userid, query[0]): raise WeasylError("UserIgnored") elif ignore and blocktag.check(userid, submitid=submitid): raise WeasylError("TagBlocked") # Get submission filename submitfile = media.get_submission_media(submitid).get( 'submission', [None])[0] # Get submission text if submitfile and submitfile['file_type'] in ['txt', 'htm']: submittext = files.read(submitfile['full_file_path']) else: submittext = None embedlink = d.text_first_line(query[5]) if "v" in query[8] else None google_doc_embed = None if 'D' in query[8]: db = d.connect() gde = d.meta.tables['google_doc_embeds'] q = (sa.select([gde.c.embed_url]).where(gde.c.submitid == submitid)) results = db.execute(q).fetchall() if not results: raise WeasylError("can't find embed information") google_doc_embed = results[0] tags, artist_tags = searchtag.select_with_artist_tags(submitid) settings = d.get_profile_settings(query[0]) if query[12] is None: sub_media = media.get_submission_media(submitid) else: sub_media = media.deserialize_image_representations(query[12]) return { "submitid": submitid, "userid": query[0], "username": query[1], "folderid": query[2], "unixtime": query[3], "title": query[4], "content": (d.text_first_line(query[5], strip=True) if "v" in query[8] else query[5]), "subtype": query[6], "rating": query[7], "settings": query[8], "page_views": (query[9] + 1 if d.common_view_content( userid, 0 if anyway == "true" else submitid, "submit") else query[9]), "fave_count": query[11], "mine": userid == query[0], "reported": report.check(submitid=submitid), "favorited": favorite.check(userid, submitid=submitid), "friends_only": "f" in query[8], "hidden_submission": "h" in query[8], "collected": collection.owns(userid, submitid), "no_request": not settings.allow_collection_requests, "text": submittext, "sub_media": sub_media, "user_media": media.get_user_media(query[0]), "submit": submitfile, "embedlink": embedlink, "embed": embed.html(embedlink) if embedlink is not None else None, "google_doc_embed": google_doc_embed, "tags": tags, "artist_tags": artist_tags, "removable_tags": searchtag.removable_tags(userid, query[0], tags, artist_tags), "can_remove_tags": searchtag.can_remove_tags(userid, query[0]), "folder_more": select_near(userid, rating, 1, query[0], query[2], submitid), "folder_title": query[10] if query[10] else "Root", "comments": comment.select(userid, submitid=submitid), }
def insert(userid, submitid=None, charid=None, journalid=None, parentid=None, content=None): if not submitid and not charid and not journalid: raise WeasylError("Unexpected") elif not content: raise WeasylError("commentInvalid") # Determine indent and parentuserid if parentid: query = d.execute( "SELECT userid, indent FROM %s WHERE commentid = %i", [ "comments" if submitid else "charcomment" if charid else "journalcomment", parentid ], options="single") if not query: raise WeasylError("Unexpected") indent = query[1] + 1 parentuserid = query[0] else: indent = 0 parentuserid = None # Determine otherid otherid = d.execute( "SELECT userid FROM %s WHERE %s = %i AND settings !~ 'h'", ["submission", "submitid", submitid] if submitid else ["character", "charid", charid] if charid else ["journal", "journalid", journalid], options="element") # Check permissions if not otherid: raise WeasylError("submissionRecordMissing") elif ignoreuser.check(otherid, userid): raise WeasylError("pageOwnerIgnoredYou") elif ignoreuser.check(userid, otherid): raise WeasylError("youIgnoredPageOwner") elif parentuserid and ignoreuser.check(parentuserid, userid): raise WeasylError("replyRecipientIgnoredYou") elif parentuserid and ignoreuser.check(userid, parentuserid): raise WeasylError("youIgnoredReplyRecipient") # Create comment if submitid: co = d.meta.tables['comments'] db = d.connect() commentid = db.scalar(co.insert().values(userid=userid, target_sub=submitid, parentid=parentid or None, content=content, unixtime=arrow.utcnow(), indent=indent).returning( co.c.commentid)) else: commentid = d.execute( "INSERT INTO %s (userid, targetid, parentid, " "content, unixtime, indent) VALUES (%i, %i, %i, '%s', %i, %i) RETURNING " "commentid", [ "charcomment" if charid else "journalcomment", userid, d.get_targetid(submitid, charid, journalid), parentid, content, d.get_time(), indent ], options="element") # Create notification if parentid and (userid != parentuserid): welcome.commentreply_insert(userid, commentid, parentuserid, parentid, submitid, charid, journalid) elif not parentid: # build a list of people this comment should notify # circular imports are cool and fun from weasyl.collection import find_owners notified = set(find_owners(submitid)) # check to see who we should deliver comment notifications to def can_notify(other): other_jsonb = d.get_profile_settings(other) allow_notify = other_jsonb.allow_collection_notifs ignored = ignoreuser.check(other, userid) return allow_notify and not ignored notified = set(filter(can_notify, notified)) # always give notification on own content notified.add(otherid) # don't give me a notification for my own comment notified.discard(userid) for other in notified: welcome.comment_insert(userid, commentid, other, submitid, charid, journalid) d.metric('increment', 'comments') return commentid
def associate(userid, tags, submitid=None, charid=None, journalid=None, preferred_tags_userid=None, optout_tags_userid=None): """ Associates searchtags with a content item. Parameters: userid: The userid of the user associating tags tags: A set of tags submitid: The ID number of a submission content item to associate ``tags`` to. (default: None) charid: The ID number of a character content item to associate ``tags`` to. (default: None) journalid: The ID number of a journal content item to associate ``tags`` to. (default: None) preferred_tags_userid: The ID number of a user to associate ``tags`` to for Preferred tags. (default: None) optout_tags_userid: The ID number of a user to associate ``tags`` to for Opt-Out tags. (default: None) Returns: A dict containing two elements. 1) ``add_failure_restricted_tags``, which contains a space separated string of tag titles which failed to be added to the content item due to the user or global restricted tag lists; and 2) ``remove_failure_owner_set_tags``, which contains a space separated string of tag titles which failed to be removed from the content item due to the owner of the aforementioned item prohibiting users from removing tags set by the content owner. If an element does not have tags, the element is set to None. If neither elements are set, the function returns None. """ targetid = d.get_targetid(submitid, charid, journalid) # Assign table, feature, ownerid if submitid: table, feature = "searchmapsubmit", "submit" ownerid = d.get_ownerid(submitid=targetid) elif charid: table, feature = "searchmapchar", "char" ownerid = d.get_ownerid(charid=targetid) elif journalid: table, feature = "searchmapjournal", "journal" ownerid = d.get_ownerid(journalid=targetid) elif preferred_tags_userid: table, feature = "artist_preferred_tags", "user" targetid = ownerid = preferred_tags_userid elif optout_tags_userid: table, feature = "artist_optout_tags", "user" targetid = ownerid = optout_tags_userid else: raise WeasylError("Unexpected") # Check permissions and invalid target if not ownerid: raise WeasylError("TargetRecordMissing") elif userid != ownerid and ("g" in d.get_config(userid) or preferred_tags_userid or optout_tags_userid): # disallow if user is forbidden from tagging, or trying to set artist tags on someone other than themselves raise WeasylError("InsufficientPermissions") elif ignoreuser.check(ownerid, userid): raise WeasylError("contentOwnerIgnoredYou") # Determine previous tagids, titles, and settings existing = d.engine.execute( "SELECT tagid, title, settings FROM {} INNER JOIN searchtag USING (tagid) WHERE targetid = %(target)s".format(table), target=targetid).fetchall() # Retrieve tag titles and tagid pairs, for new (if any) and existing tags query = add_and_get_searchtags(tags) existing_tagids = {t.tagid for t in existing} entered_tagids = {t.tagid for t in query} # Assign added and removed added = entered_tagids - existing_tagids removed = existing_tagids - entered_tagids # enforce the limit on artist preference tags if preferred_tags_userid and (len(added) - len(removed) + len(existing)) > MAX_PREFERRED_TAGS: raise WeasylError("tooManyPreferenceTags") # Track which tags fail to be added or removed to later notify the user (Note: These are tagids at this stage) add_failure_restricted_tags = None remove_failure_owner_set_tags = None # If the modifying user is not the owner of the object, and is not staff, check user/global restriction lists if userid != ownerid and userid not in staff.MODS: user_rtags = set(query_user_restricted_tags(ownerid)) global_rtags = set(query_global_restricted_tags()) add_failure_restricted_tags = remove_restricted_tags(user_rtags | global_rtags, query) added -= add_failure_restricted_tags if len(add_failure_restricted_tags) == 0: add_failure_restricted_tags = None # Check removed artist tags if not can_remove_tags(userid, ownerid): existing_artist_tags = {t.tagid for t in existing if 'a' in t.settings} remove_failure_owner_set_tags = removed & existing_artist_tags removed.difference_update(existing_artist_tags) entered_tagids.update(existing_artist_tags) # Submission items use a different method of tag protection for artist tags; ignore them if submitid or len(remove_failure_owner_set_tags) == 0: remove_failure_owner_set_tags = None # Remove tags if removed: d.engine.execute( "DELETE FROM {} WHERE targetid = %(target)s AND tagid = ANY (%(removed)s)".format(table), target=targetid, removed=list(removed)) if added: d.engine.execute( "INSERT INTO {} SELECT tag, %(target)s FROM UNNEST (%(added)s) AS tag".format(table), target=targetid, added=list(added)) # preference/optout tags can only be set by the artist, so this settings column does not apply if userid == ownerid and not (preferred_tags_userid or optout_tags_userid): d.execute( "UPDATE %s SET settings = settings || 'a' WHERE targetid = %i AND tagid IN %s", [table, targetid, d.sql_number_list(list(added))]) if submitid: d.engine.execute( 'INSERT INTO submission_tags (submitid, tags) VALUES (%(submission)s, %(tags)s) ' 'ON CONFLICT (submitid) DO UPDATE SET tags = %(tags)s', submission=submitid, tags=list(entered_tagids)) db = d.connect() db.execute( d.meta.tables['tag_updates'].insert() .values(submitid=submitid, userid=userid, added=tag_array(added), removed=tag_array(removed))) if userid != ownerid: welcome.tag_update_insert(ownerid, submitid) files.append( "%stag.%s.%s.log" % (m.MACRO_SYS_LOG_PATH, feature, d.get_timestamp()), "-%sID %i -T %i -UID %i -X %s\n" % (feature[0].upper(), targetid, d.get_time(), userid, " ".join(tags))) # Return dict with any tag titles as a string that failed to be added or removed if add_failure_restricted_tags or remove_failure_owner_set_tags: if add_failure_restricted_tags: add_failure_restricted_tags = " ".join({tag.title for tag in query if tag.tagid in add_failure_restricted_tags}) if remove_failure_owner_set_tags: remove_failure_owner_set_tags = " ".join({tag.title for tag in existing if tag.tagid in remove_failure_owner_set_tags}) return {"add_failure_restricted_tags": add_failure_restricted_tags, "remove_failure_owner_set_tags": remove_failure_owner_set_tags} else: return None
def insert(userid, submitid=None, charid=None, journalid=None, updateid=None, parentid=None, content=None): if submitid: table = "comments" elif charid: table = "charcomment" elif journalid: table = "journalcomment" elif updateid: table = "siteupdatecomment" else: raise WeasylError("Unexpected") if not content: raise WeasylError("commentInvalid") # Determine parent userid if parentid: parentuserid = d.engine.scalar( "SELECT userid FROM {table} WHERE commentid = %(parent)s".format( table=table), parent=parentid, ) if parentuserid is None: raise WeasylError("Unexpected") else: if updateid: parentid = None # parentid == 0 parentuserid = None if updateid: otherid = d.engine.scalar( "SELECT userid FROM siteupdate WHERE updateid = %(update)s", update=updateid) if not otherid: raise WeasylError("submissionRecordMissing") else: # Determine the owner of the target otherid = d.engine.scalar( "SELECT userid FROM %s WHERE %s = %i AND settings !~ 'h'" % (("submission", "submitid", submitid) if submitid else ("character", "charid", charid) if charid else ("journal", "journalid", journalid))) # Check permissions if not otherid: raise WeasylError("submissionRecordMissing") elif ignoreuser.check(otherid, userid): raise WeasylError("pageOwnerIgnoredYou") elif ignoreuser.check(userid, otherid): raise WeasylError("youIgnoredPageOwner") if parentuserid and ignoreuser.check(parentuserid, userid): raise WeasylError("replyRecipientIgnoredYou") elif parentuserid and ignoreuser.check(userid, parentuserid): raise WeasylError("youIgnoredReplyRecipient") # Create comment if submitid: co = d.meta.tables['comments'] db = d.connect() commentid = db.scalar(co.insert().values( userid=userid, target_sub=submitid, parentid=parentid or None, content=content, unixtime=arrow.utcnow()).returning(co.c.commentid)) elif updateid: commentid = d.engine.scalar( "INSERT INTO siteupdatecomment (userid, targetid, parentid, content)" " VALUES (%(user)s, %(update)s, %(parent)s, %(content)s)" " RETURNING commentid", user=userid, update=updateid, parent=parentid, content=content, ) else: commentid = d.engine.scalar( "INSERT INTO {table} (userid, targetid, parentid, content, unixtime)" " VALUES (%(user)s, %(target)s, %(parent)s, %(content)s, %(now)s)" " RETURNING commentid".format( table="charcomment" if charid else "journalcomment"), user=userid, target=d.get_targetid(charid, journalid), parent=parentid or 0, content=content, now=d.get_time(), ) # Create notification if parentid and (userid != parentuserid): welcome.commentreply_insert(userid, commentid, parentuserid, parentid, submitid, charid, journalid, updateid) elif not parentid: # build a list of people this comment should notify # circular imports are cool and fun from weasyl.collection import find_owners notified = set(find_owners(submitid)) # check to see who we should deliver comment notifications to def can_notify(other): other_jsonb = d.get_profile_settings(other) allow_notify = other_jsonb.allow_collection_notifs ignored = ignoreuser.check(other, userid) return allow_notify and not ignored notified = set(filter(can_notify, notified)) # always give notification on own content notified.add(otherid) # don't give me a notification for my own comment notified.discard(userid) for other in notified: welcome.comment_insert(userid, commentid, other, submitid, charid, journalid, updateid) d.metric('increment', 'comments') return commentid
def insert(userid, submitid=None, charid=None, journalid=None, parentid=None, content=None): if not submitid and not charid and not journalid: raise WeasylError("Unexpected") elif not content: raise WeasylError("commentInvalid") # Determine indent and parentuserid if parentid: query = d.execute("SELECT userid, indent FROM %s WHERE commentid = %i", ["comments" if submitid else "charcomment" if charid else "journalcomment", parentid], options="single") if not query: raise WeasylError("Unexpected") indent = query[1] + 1 parentuserid = query[0] else: indent = 0 parentuserid = None # Determine otherid otherid = d.execute("SELECT userid FROM %s WHERE %s = %i AND settings !~ 'h'", ["submission", "submitid", submitid] if submitid else ["character", "charid", charid] if charid else ["journal", "journalid", journalid], options="element") # Check permissions if not otherid: raise WeasylError("submissionRecordMissing") elif ignoreuser.check(otherid, userid): raise WeasylError("pageOwnerIgnoredYou") elif ignoreuser.check(userid, otherid): raise WeasylError("youIgnoredPageOwner") elif parentuserid and ignoreuser.check(parentuserid, userid): raise WeasylError("replyRecipientIgnoredYou") elif parentuserid and ignoreuser.check(userid, parentuserid): raise WeasylError("youIgnoredReplyRecipient") # Create comment if submitid: co = d.meta.tables['comments'] db = d.connect() commentid = db.scalar( co.insert() .values(userid=userid, target_sub=submitid, parentid=parentid or None, content=content, unixtime=arrow.utcnow(), indent=indent) .returning(co.c.commentid)) else: commentid = d.execute( "INSERT INTO %s (userid, targetid, parentid, " "content, unixtime, indent) VALUES (%i, %i, %i, '%s', %i, %i) RETURNING " "commentid", [ "charcomment" if charid else "journalcomment", userid, d.get_targetid(submitid, charid, journalid), parentid, content, d.get_time(), indent ], options="element") # Create notification if parentid and (userid != parentuserid): welcome.commentreply_insert(userid, commentid, parentuserid, parentid, submitid, charid, journalid) elif not parentid: # build a list of people this comment should notify # circular imports are cool and fun from weasyl.collection import find_owners notified = set(find_owners(submitid)) # check to see who we should deliver comment notifications to def can_notify(other): other_jsonb = d.get_profile_settings(other) allow_notify = other_jsonb.allow_collection_notifs ignored = ignoreuser.check(other, userid) return allow_notify and not ignored notified = set(filter(can_notify, notified)) # always give notification on own content notified.add(otherid) # don't give me a notification for my own comment notified.discard(userid) for other in notified: welcome.comment_insert(userid, commentid, other, submitid, charid, journalid) d.metric('increment', 'comments') return commentid
def can_notify(other): other_jsonb = d.get_profile_settings(other) allow_notify = other_jsonb.allow_collection_notifs ignored = ignoreuser.check(other, userid) return allow_notify and not ignored
def insert(userid, submitid=None, charid=None, journalid=None): if submitid: content_table, id_field, target = "submission", "submitid", submitid elif charid: content_table, id_field, target = "character", "charid", charid else: content_table, id_field, target = "journal", "journalid", journalid query = d.engine.execute( "SELECT userid, settings FROM %s WHERE %s = %i" % (content_table, id_field, target), ).first() if not query: raise WeasylError("TargetRecordMissing") elif userid == query[0]: raise WeasylError("CannotSelfFavorite") elif "f" in query[1] and not frienduser.check(userid, query[0]): raise WeasylError("FriendsOnly") elif ignoreuser.check(userid, query[0]): raise WeasylError("YouIgnored") elif ignoreuser.check(query[0], userid): raise WeasylError("contentOwnerIgnoredYou") notified = [] def insert_transaction(db): insert_result = db.execute( 'INSERT INTO favorite (userid, targetid, type, unixtime) ' 'VALUES (%(user)s, %(target)s, %(type)s, %(now)s) ' 'ON CONFLICT DO NOTHING', user=userid, target=d.get_targetid(submitid, charid, journalid), type='s' if submitid else 'f' if charid else 'j', now=d.get_time()) if insert_result.rowcount == 0: return if submitid: db.execute( "UPDATE submission SET favorites = favorites + 1" " WHERE submitid = %(target)s", target=submitid, ) if not notified: # create a list of users to notify notified_ = collection.find_owners(submitid) # conditions under which "other" should be notified def can_notify(other): other_jsonb = d.get_profile_settings(other) allow_notify = other_jsonb.allow_collection_notifs return allow_notify and not ignoreuser.check(other, userid) notified.extend(u for u in notified_ if can_notify(u)) # always notify for own content notified.append(query[0]) for other in notified: welcome.favorite_insert(db, userid, submitid=submitid, charid=charid, journalid=journalid, otherid=other) d.serializable_retry(insert_transaction)