def create_generic(userid, submission, **kwargs): tags = kwargs['tags'] if submission.subtype not in valid_types: submission.subtype = expected_type * 1000 + 999 if not submission.title: raise WeasylError("titleInvalid") elif not submission.rating: raise WeasylError("ratingInvalid") elif len(tags) < 2: raise WeasylError("notEnoughTags") elif not folder.check(userid, submission.folderid): raise WeasylError("Unexpected") profile.check_user_rating_allowed(userid, submission.rating) newid = create_specific( userid=userid, submission=submission, **kwargs) if newid: p = d.meta.tables['profile'] d.connect().execute(p.update().where(p.c.userid == userid).values(latest_submission_time=arrow.utcnow())) return newid
def create_user(full_name="", birthday=arrow.get(586162800), config=None, username=None, password=None, email_addr=None, user_id=None): """ Creates a new user and profile, and returns the user ID. """ if username is None: username = "******" + str(next(_user_index)) while True: user = add_entity(users.Login(login_name=legacy.login_name(username), last_login=arrow.get(0))) if user.userid not in staff.MODS and user.userid not in staff.DEVELOPERS: break db = d.connect() db.delete(user) db.flush() add_entity(users.Profile(userid=user.userid, username=username, full_name=full_name, unixtime=arrow.get(0), config=config)) add_entity(users.UserInfo(userid=user.userid, birthday=birthday)) # Set a password for this user if password is not None: d.engine.execute("INSERT INTO authbcrypt VALUES (%(id)s, %(bcrypthash)s)", id=user.userid, bcrypthash=login.passhash(password)) # Set an email address for this user if email_addr is not None: d.engine.execute("UPDATE login SET email = %(email)s WHERE userid = %(id)s", email=email_addr, id=user.userid) # Force the userID to a user-defined value and return it if user_id is not None: d.engine.execute("UPDATE login SET userid = %(newid)s WHERE userid = %(oldid)s", newid=user_id, oldid=user.userid) return user_id return user.userid
def main(): db = d.connect() q = ( sa.select([sa.func.array_agg(orm.MediaItem.mediaid)]) .where(orm.MediaItem.sha256 != None) .group_by(orm.MediaItem.sha256) .having(sa.func.count() > 1)) [[count]] = db.execute( sa.select([sa.func.count()]) .select_from(q.alias('query')) ).fetchall() for e, (mediaids,) in enumerate(db.execute(q), start=1): sys.stdout.write('\r%d/%d' % (e, count)) sys.stdout.flush() old_media_item = db.query(orm.DiskMediaItem).get(mediaids[0]) new_media_item = orm.DiskMediaItem( sha256=old_media_item.sha256, file_type=old_media_item.file_type) with open(old_media_item.full_file_path, 'rb') as infile: data = infile.read() new_media_item.init_from_data(data) db.add(new_media_item) db.flush() for table, column in [('submission_media_links', 'mediaid'), ('user_media_links', 'mediaid'), ('media_media_links', 'described_with_id'), ('media_media_links', 'describee_id')]: table_obj = d.meta.tables[table] q = ( table_obj.update() .values(**{column: new_media_item.mediaid}) .where(table_obj.c[column].in_(mediaids))) db.execute(q) print
def main(): db = define.connect() all_media = sa.union( sa.select([orm.UserMediaLink.mediaid]), sa.select([orm.SubmissionMediaLink.mediaid]), sa.select([orm.MediaMediaLink.describee_id]), sa.select([orm.MediaMediaLink.described_with_id]), ).alias('all_media') q = ( db.query(orm.MediaItem) .with_polymorphic([orm.DiskMediaItem]) .outerjoin(all_media) .filter(all_media.c.mediaid == None)) count = q.count() for e, media_item in enumerate(q, start=1): sys.stdout.write('\r%d/%d' % (e, count)) sys.stdout.flush() db.delete(media_item) try: os.unlink(media_item.full_file_path) except OSError as e: if e.errno == errno.ENOENT: continue raise db.flush() print
def finduser(userid, form): form.userid = d.get_int(form.userid) lo = d.meta.tables['login'] sh = d.meta.tables['comments'] q = d.sa.select([ lo.c.userid, lo.c.login_name, lo.c.email, (d.sa.select([d.sa.func.count()]) .select_from(sh) .where(sh.c.target_user == lo.c.userid) .where(sh.c.settings.op('~')('s'))).label('staff_notes'), ]) if form.userid: q = q.where(lo.c.userid == form.userid) elif form.username: q = q.where(lo.c.login_name.op('~')(form.username)) elif form.email: q = q.where(d.sa.or_( lo.c.email.op('~')(form.email), lo.c.email.op('ilike')('%%%s%%' % form.email), )) else: return [] q = q.limit(100).order_by(lo.c.login_name.asc()) db = d.connect() return db.execute(q)
def main(): do_users, min_submission = sys.argv[1:] db = connect() if do_users == 'yes': print 'starting users' port_users(db) print 'starting submissions' port_submissions(db, int(min_submission))
def check_for_duplicate_media(userid, mediaid): db = d.connect() q = ( db.query(orm.Submission) .filter_by(userid=userid, is_hidden=False) .join(orm.SubmissionMediaLink) .filter_by(mediaid=mediaid, link_type='submission')) if q.first(): raise WeasylError('duplicateSubmission')
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 tag_history(submitid): db = d.connect() tu = d.meta.tables['tag_updates'] pr = d.meta.tables['profile'] return db.execute( sa.select([pr.c.username, tu.c.updated_at, tu.c.added, tu.c.removed]) .select_from(tu.join(pr, tu.c.userid == pr.c.userid)) .where(tu.c.submitid == submitid) .order_by(tu.c.updated_at.desc()))
def edit_preferences(userid, timezone=None, preferences=None, jsonb_settings=None): """ Apply changes to stored preferences for a given user. :param userid: The userid to apply changes to :param timezone: (optional) new Timezone to set for user :param preferences: (optional) old-style char preferences, overwrites all previous settings :param jsonb_settings: (optional) JSON preferences, overwrites all previous settings :return: None """ config = d.get_config(userid) tooyoung = False if preferences is not None: tooyoung |= get_user_age(userid) < preferences.rating.minimum_age if jsonb_settings is not None: sfwrating = jsonb_settings.max_sfw_rating sfwrating = ratings.CODE_MAP.get(sfwrating, ratings.GENERAL) tooyoung |= get_user_age(userid) < sfwrating.minimum_age if tooyoung: raise WeasylError("birthdayInsufficient") if timezone is not None and timezone not in pytz.all_timezones: raise WeasylError('invalidTimezone') db = d.connect() updates = {} if preferences is not None: # update legacy preferences # clear out the option codes that are being replaced. for i in Config.all_option_codes: config = config.replace(i, "") config_str = config + preferences.to_code() updates['config'] = config_str d._get_config.invalidate(userid) if jsonb_settings is not None: # update jsonb preferences updates['jsonb_settings'] = jsonb_settings.get_raw() d._get_profile_settings.invalidate(userid) d.engine.execute( tables.profile.update().where(tables.profile.c.userid == userid), updates ) # update TZ if timezone is not None: tz = db.query(orm.UserTimezone).get(userid) if tz is None: tz = orm.UserTimezone(userid=userid) db.add(tz) tz.timezone = timezone db.flush() tz.cache() else: db.flush()
def create_submission_tag(tagid, targetid, settings=None): db = d.connect() db.add( content.SubmissionTag(tagid=tagid, targetid=targetid, settings=settings)) db.flush() db.execute( 'INSERT INTO submission_tags (submitid, tags) VALUES (:submission, ARRAY[:tag]) ' 'ON CONFLICT (submitid) DO UPDATE SET tags = submission_tags.tags || :tag', {'submission': targetid, 'tag': tagid})
def editprofiletext(userid, otherid, content): pr = d.meta.tables['profile'] db = d.connect() condition = pr.c.userid == otherid previous_profile = db.scalar(sa.select([pr.c.profile_text]).where(condition)) db.execute(pr.update().where(condition).values(profile_text=content)) note_about( userid, otherid, 'Profile text replaced with:', '%s\n\n## Profile text was:\n\n%s' % (content, previous_profile))
def editcatchphrase(userid, otherid, content): pr = d.meta.tables['profile'] db = d.connect() condition = pr.c.userid == otherid previous_catchphrase = db.scalar(sa.select([pr.c.catchphrase]).where(condition)) db.execute(pr.update().where(condition).values(catchphrase=content)) note_about( userid, otherid, 'Catchphrase replaced with:', '%s\n\n## Catchphrase was:\n\n%s' % (content, previous_catchphrase))
def count(ownerid, staffnotes=False): db = d.connect() sh = d.meta.tables['comments'] op = '~' if staffnotes else '!~' q = ( d.sa.select([d.sa.func.count()]) .select_from(sh) .where(sh.c.settings.op(op)('s')) .where(sh.c.target_user == ownerid)) (ret,), = db.execute(q) return ret
def create_session(user): """ Creates a session for a user and returns the corresponding WZL cookie. """ session = sessions.create_session(user) db = d.connect() db.add(session) db.flush() return 'WZL=' + session.sessionid.encode('utf-8')
def test_verify_login_record_is_updated(): # Use a fake session for this test. user_id = db_utils.create_user() sess = get_current_request().weasyl_session = create_session(user_id) db = d.connect() db.add(sess) db.flush() d.engine.execute("UPDATE login SET last_login = -1 WHERE userid = %(id)s", id=user_id) login.signin(get_current_request(), user_id) last_login = d.engine.scalar("SELECT last_login FROM login WHERE userid = %(id)s", id=user_id) assert last_login > -1
def note_about(userid, target_user, title, message=None): staff_note = '## ' + title if message: staff_note = '%s\n\n%s' % (staff_note, message) db = d.connect() db.execute( d.meta.tables['comments'].insert() .values( userid=userid, target_user=target_user, unixtime=arrow.utcnow(), settings='s', content=staff_note, ))
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 create_session(user): """ Creates a session for a user and returns the corresponding WZL cookie. """ session = orm.Session() session.sessionid = security.generate_key(64) session.userid = user db = d.connect() db.add(session) db.flush() return 'WZL=' + session.sessionid.encode('utf-8')
def db(request): db = define.connect() def tear_down(): """ Clears all rows from the test database. """ for k, cls in registry.iteritems(): if not k[0].isupper(): continue db.query(cls).delete() db.flush() request.addfinalizer(tear_down) return db
def select_list(map_table, targetids): if not targetids: return {} mt = map_table q = ( d.sa .select([mt.c.targetid, d.sa.func.array_agg(mt.c.tagid)]) .select_from(mt) .where(mt.c.targetid.in_(targetids)) .group_by(mt.c.targetid)) db = d.connect() return dict(list(db.execute(q)))
def create(userid, x1, y1, x2, y2, auto=False, config=None): x1, y1, x2, y2 = d.get_int(x1), d.get_int(y1), d.get_int(x2), d.get_int(y2) db = d.connect() im = db.query(orm.MediaItem).get(avatar_source(userid)['mediaid']).as_image() file_type = image.image_file_type(im) bounds = None size = im.size.width, im.size.height if not auto and image.check_crop(size, x1, y1, x2, y2): bounds = geometry.Rectangle(x1, y1, x2, y2) thumb = image.shrinkcrop(im, geometry.Size(100, 100), bounds) media_item = orm.fetch_or_create_media_item( thumb.to_buffer(format=file_type), file_type=file_type, im=thumb) orm.UserMediaLink.make_or_replace_link(userid, 'avatar', media_item) orm.UserMediaLink.clear_link(userid, 'avatar-source')
def render_form(self, scopes, credentials, mobile, error=None, username='', password='', remember_me=False, not_me=False): db = d.connect() client = db.query(orm.OAuthConsumer).get(credentials['client_id']) if self.user_id: user = db.query(orm.Login).get(self.user_id) user_media = media.get_user_media(self.user_id) else: user = user_media = None credentials['scopes'] = scopes return d.render('oauth2/authorize.html', [ scopes, credentials, client, user, user_media, mobile, error, username, password, remember_me, not_me, ])
def verify(token, ip_address=None): lo = d.meta.tables["login"] lc = d.meta.tables["logincreate"] query = d.engine.execute(lc.select().where(lc.c.token == token)).first() # Did the token match a pending login create record? if not query: raise WeasylError("logincreateRecordMissing") elif query.invalid: # If the record is explicitly marked as invalid, treat the record as if it doesn't exist. raise WeasylError("logincreateRecordMissing") db = d.connect() with db.begin(): # Create login record userid = db.scalar(lo.insert().returning(lo.c.userid), { "login_name": d.get_sysname(query.username), "last_login": arrow.now(), "email": query.email, "ip_address_at_signup": ip_address, }) # Create profile records db.execute(d.meta.tables["authbcrypt"].insert(), { "userid": userid, "hashsum": query.hashpass, }) db.execute(d.meta.tables["profile"].insert(), { "userid": userid, "username": query.username, "full_name": query.username, "unixtime": arrow.now(), "config": "kscftj", }) db.execute(d.meta.tables["userinfo"].insert(), { "userid": userid, "birthday": query.birthday, }) db.execute(d.meta.tables["userstats"].insert(), { "userid": userid, }) db.execute(d.meta.tables["welcomecount"].insert(), { "userid": userid, }) # Update logincreate records db.execute(lc.delete().where(lc.c.token == token)) d.metric('increment', 'verifiedusers')
def select_list(map_table, targetids): if not targetids: return {} mt = map_table st = d.meta.tables['searchtag'] q = ( d.sa .select([mt.c.targetid, d.sa.func.array_agg(st.c.title)]) .select_from(mt.join(st, mt.c.tagid == st.c.tagid)) .where(mt.c.targetid.in_(targetids)) .group_by(mt.c.targetid)) db = d.connect() return dict(list(db.execute(q)))
def select_with_artist_tags(submitid): db = d.connect() tags = ( db.query(orm.Tag.title, orm.SubmissionTag.is_artist_tag) .join(orm.SubmissionTag) .filter_by(targetid=submitid) .order_by(orm.Tag.title) .all()) ret = [] artist_tags = set() for tag, is_artist_tag in tags: ret.append(tag) if is_artist_tag: artist_tags.add(tag) return ret, artist_tags
def db(request): db = define.connect() def tear_down(): """ Clears all rows from the test database. """ db.flush() for table in metadata.tables.values(): db.execute(table.delete()) request.addfinalizer(tear_down) if request.cls is not None: request.cls.db = db return db
def select_friends(userid, otherid, limit=None, backid=None, nextid=None, choose=False): """ Return accepted friends. If `choose` is an integer, results will be ordered randomly. """ fr = d.meta.tables['frienduser'] pr = d.meta.tables['profile'] iu = d.meta.tables['ignoreuser'] friends = d.sa.union( (d.sa .select([fr.c.otherid, pr.c.username, pr.c.config]) .select_from(fr.join(pr, fr.c.otherid == pr.c.userid)) .where(d.sa.and_(fr.c.userid == otherid, fr.c.settings.op('!~')('p')))), (d.sa .select([fr.c.userid, pr.c.username, pr.c.config]) .select_from(fr.join(pr, fr.c.userid == pr.c.userid)) .where(d.sa.and_(fr.c.otherid == otherid, fr.c.settings.op('!~')('p'))))) friends = friends.alias('friends') query = d.sa.select(friends.c) if userid: query = query.where( ~friends.c.otherid.in_(d.sa.select([iu.c.otherid]).where(iu.c.userid == userid))) if backid: query = query.where( friends.c.username < d.sa.select([pr.c.username]).where(pr.c.userid == backid)) elif nextid: query = query.where( friends.c.username > d.sa.select([pr.c.username]).where(pr.c.userid == nextid)) if choose: query = query.order_by('RANDOM()') else: query = query.order_by( friends.c.username.desc() if backid else friends.c.username.asc()) query = query.limit(limit) db = d.connect() query = [{ "userid": r.otherid, "username": r.username, } for r in db.execute(query)] ret = (d.get_random_set(query, choose) if choose else query[::-1] if backid else query) media.populate_with_user_media(ret) return ret
def select_by_id(updateid): su = d.meta.tables['siteupdate'] pr = d.meta.tables['profile'] q = ( sa.select([ pr.c.userid, pr.c.username, su.c.title, su.c.content, su.c.unixtime, ]) .select_from(su.join(pr, su.c.userid == pr.c.userid)) .where(su.c.updateid == updateid)) db = d.connect() results = db.execute(q).fetchall() if not results: raise WeasylError('RecordMissing') results = dict(results[0]) results['user_media'] = media.get_user_media(results['userid']) results['timestamp'] = results['unixtime'].timestamp + UNIXTIME_OFFSET return results
def run_periodic_tasks(): web.ctx.clear() now = arrow.utcnow() time_now = get_time() db = connect().connection() with db.begin(): locked = db.scalar("SELECT pg_try_advisory_xact_lock(0)") if not locked: return last_run = arrow.get(db.scalar("SELECT last_run FROM cron_runs")) if not last_run or now < last_run.replace(seconds=59): return # Recache the latest submissions # Every 2 minutes if now.minute % 2 == 0: index.recent_submissions.refresh() log.msg('refreshed recent submissions') # Recache the active user counts # Every 5 minutes if now.minute % 5 == 0: active_users.refresh() log.msg('refreshed active user counts') # Recalculate recently popular submissions # Every 10 minutes if now.minute % 10 == 0: submission.select_recently_popular.refresh() log.msg('refreshed recently popular submissions') # Delete all records from contentview table # Every 15 minutes if now.minute % 15 == 0: db.execute("DELETE FROM views") log.msg('cleared views') # Delete password resets older than one day # Daily at 0:00 if now.hour == 0 and now.minute == 0: db.execute("DELETE FROM forgotpassword WHERE set_time < %(expiry)s", expiry=time_now - 86400) log.msg('cleared old forgotten password requests') db.execute("UPDATE cron_runs SET last_run = %(now)s", now=now.naive)
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 create_multimedia(userid, submission, embedlink=None, friends_only=None, tags=None, coverfile=None, thumbfile=None, submitfile=None, critique=False, create_notifications=True, auto_thumb=False): embedlink = embedlink.strip() # Determine filesizes coversize = len(coverfile) thumbsize = len(thumbfile) submitsize = len(submitfile) if not submitsize and not embedlink: raise WeasylError("submitSizeZero") elif embedlink and not embed.check_valid(embedlink): raise WeasylError("embedlinkInvalid") elif coversize > 10 * _MEGABYTE: raise WeasylError("coverSizeExceedsLimit") elif thumbsize > 10 * _MEGABYTE: raise WeasylError("thumbSizeExceedsLimit") if submitsize: submitextension = files.get_extension_for_category( submitfile, m.MULTIMEDIA_SUBMISSION_CATEGORY) if submitextension is None: raise WeasylError("submitType") elif submitextension not in [".mp3", ".swf"] and not embedlink: raise WeasylError("submitType") elif _limit(submitsize, submitextension): raise WeasylError("submitSizeExceedsLimit") submit_media_item = orm.MediaItem.fetch_or_create( submitfile, file_type=submitextension.lstrip('.')) check_for_duplicate_media(userid, submit_media_item.mediaid) else: submit_media_item = None thumb_media_item = media.make_cover_media_item(thumbfile) cover_media_item = media.make_cover_media_item(coverfile) if cover_media_item and not thumb_media_item: thumb_media_item = cover_media_item tempthumb_media_item = None im = None if auto_thumb: if thumbsize == 0 and coversize == 0: # Fetch default thumbnail from source if available thumb_url = embed.thumbnail(embedlink) if thumb_url: resp = d.http_get(thumb_url, timeout=5) im = image.from_string(resp.content) if not im and (thumbsize or coversize): im = image.from_string(thumbfile or coverfile) if im: tempthumb = images.make_thumbnail(im) tempthumb_type = images.image_file_type(tempthumb) tempthumb_media_item = orm.MediaItem.fetch_or_create( tempthumb.to_buffer(format=tempthumb_type), file_type=tempthumb_type, im=tempthumb) # Assign settings settings = [] settings.append("f" if friends_only else "") settings.append("q" if critique else "") settings.append("v" if embedlink else "") settings = "".join(settings) # Inject embedlink if embedlink: submission.content = "".join([embedlink, "\n", submission.content]) # Create submission db = d.connect() now = arrow.get() q = (d.meta.tables['submission'].insert().values([{ "folderid": submission.folderid, "userid": userid, "unixtime": now, "title": submission.title, "content": submission.content, "subtype": submission.subtype, "rating": submission.rating, "settings": settings, "favorites": 0, "submitter_ip_address": submission.submitter_ip_address, "submitter_user_agent_id": submission.submitter_user_agent_id, }]).returning(d.meta.tables['submission'].c.submitid)) submitid = db.scalar(q) # Assign search tags searchtag.associate(userid, tags, submitid=submitid) if submit_media_item: orm.SubmissionMediaLink.make_or_replace_link(submitid, 'submission', submit_media_item) if cover_media_item: orm.SubmissionMediaLink.make_or_replace_link(submitid, 'cover', cover_media_item) if thumb_media_item: orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-source', thumb_media_item) if tempthumb_media_item: orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-custom', tempthumb_media_item) # Create notifications if create_notifications: _create_notifications(userid, submitid, submission.rating, settings) d.metric('increment', 'submissions') d.metric('increment', 'multimediasubmissions') return submitid, bool(thumb_media_item)
def create_character_tag(tagid, targetid, settings=None): db = d.connect() db.add( content.CharacterTag(tagid=tagid, targetid=targetid, settings=settings)) db.flush()
def create_visual(userid, submission, friends_only, tags, imageURL, thumbfile, submitfile, critique, create_notifications): premium = d.get_premium(userid) if imageURL: resp = d.http_get(imageURL, timeout=5) submitfile = resp.content # Determine filesizes thumbsize = len(thumbfile) submitsize = len(submitfile) if not submitsize: files.clear_temporary(userid) raise WeasylError("submitSizeZero") elif thumbsize > 10 * _MEGABYTE: files.clear_temporary(userid) raise WeasylError("thumbSizeExceedsLimit") im = image.from_string(submitfile) submitextension = image.image_extension(im) if _limit(submitsize, submitextension, premium): raise WeasylError("submitSizeExceedsLimit") elif submitextension not in [".jpg", ".png", ".gif"]: raise WeasylError("submitType") submit_file_type = submitextension.lstrip('.') submit_media_item = orm.fetch_or_create_media_item( submitfile, file_type=submit_file_type, im=im) check_for_duplicate_media(userid, submit_media_item.mediaid) cover_media_item = submit_media_item.ensure_cover_image(im) # Thumbnail stuff. # Always create a 'generated' thumbnail from the source image. thumb_generated = images.make_thumbnail(im) if thumb_generated is im: thumb_generated_media_item = submit_media_item else: thumb_generated_media_item = orm.fetch_or_create_media_item( thumb_generated.to_buffer(format=submit_file_type), file_type=submit_file_type, im=thumb_generated) # If requested, also create a 'custom' thumbnail. thumb_media_item = media.make_cover_media_item(thumbfile) if thumb_media_item: thumb_custom = images.make_thumbnail(image.from_string(thumbfile)) thumb_custom_media_item = orm.fetch_or_create_media_item( thumb_custom.to_buffer(format=submit_file_type), file_type=submit_file_type, im=thumb_custom) # Assign settings settings = [] settings.append("f" if friends_only else "") settings.append("q" if critique else "") settings = "".join(settings) # TODO(kailys): maintain ORM object db = d.connect() now = arrow.get() q = ( d.meta.tables['submission'].insert().values([{ "folderid": submission.folderid, "userid": userid, "unixtime": now, "title": submission.title, "content": submission.content, "subtype": submission.subtype, "rating": submission.rating.code, "settings": settings, "sorttime": now, }]).returning(d.meta.tables['submission'].c.submitid)) submitid = db.scalar(q) orm.SubmissionMediaLink.make_or_replace_link( submitid, 'submission', submit_media_item) orm.SubmissionMediaLink.make_or_replace_link( submitid, 'cover', cover_media_item) orm.SubmissionMediaLink.make_or_replace_link( submitid, 'thumbnail-generated', thumb_generated_media_item) if thumb_media_item: orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-source', thumb_media_item) orm.SubmissionMediaLink.make_or_replace_link( submitid, 'thumbnail-custom', thumb_custom_media_item) # Assign search tags searchtag.associate(userid, tags, submitid=submitid) # Create notifications if create_notifications: _create_notifications(userid, submitid, submission.rating, settings, submission.title, tags) d.metric('increment', 'submissions') d.metric('increment', 'visualsubmissions') return submitid
def finduser(targetid, username, email, dateafter, datebefore, excludesuspended, excludebanned, excludeactive, ipaddr, row_offset): targetid = d.get_int(targetid) # If we don't have any of these variables, nothing will be displayed. So fast-return an empty list. if not targetid and not username and not email and not dateafter \ and not datebefore and not excludesuspended and not excludebanned \ and not excludeactive and not ipaddr: return [] lo = d.meta.tables['login'] sh = d.meta.tables['comments'] pr = d.meta.tables['profile'] sess = d.meta.tables['sessions'] permaban = d.meta.tables['permaban'] suspension = d.meta.tables['suspension'] is_banned = d.sa.exists( d.sa.select([]) .select_from(permaban) .where(permaban.c.userid == lo.c.userid) ).label('is_banned') is_suspended = d.sa.exists( d.sa.select([]) .select_from(suspension) .where(suspension.c.userid == lo.c.userid) ).label('is_suspended') q = d.sa.select([ lo.c.userid, lo.c.login_name, lo.c.email, (d.sa.select([d.sa.func.count()]) .select_from(sh) .where(sh.c.target_user == lo.c.userid) .where(sh.c.settings.op('~')('s'))).label('staff_notes'), is_banned, is_suspended, lo.c.ip_address_at_signup, (d.sa.select([sess.c.ip_address]) .select_from(sess) .where(lo.c.userid == sess.c.userid) .limit(1) .order_by(sess.c.created_at.desc()) .correlate(sess) ).label('ip_address_session'), ]).select_from( lo.join(pr, lo.c.userid == pr.c.userid) .join(sess, sess.c.userid == pr.c.userid, isouter=True) ) # Is there a better way to only select unique accounts, when _also_ joining sessions? This _does_ work, though. q = q.distinct(lo.c.login_name) if targetid: q = q.where(lo.c.userid == targetid) elif username: q = q.where(lo.c.login_name.op('~')(username)) elif email: q = q.where(d.sa.or_( lo.c.email.op('~')(email), lo.c.email.op('ilike')('%%%s%%' % email), )) # Filter for banned and/or suspended accounts if excludeactive == "on": q = q.where(is_banned | is_suspended) if excludebanned == "on": q = q.where(~is_banned) if excludesuspended == "on": q = q.where(~is_suspended) # Filter for IP address if ipaddr: q = q.where(d.sa.or_( lo.c.ip_address_at_signup.op('ilike')('%s%%' % ipaddr), sess.c.ip_address.op('ilike')('%s%%' % ipaddr) )) # Filter for date-time if dateafter and datebefore: q = q.where(d.sa.between(pr.c.created_at, arrow.get(dateafter).datetime, arrow.get(datebefore).datetime)) elif dateafter: q = q.where(pr.c.created_at >= arrow.get(dateafter).datetime) elif datebefore: q = q.where(pr.c.created_at <= arrow.get(datebefore).datetime) # Apply any row offset if row_offset: q = q.offset(row_offset) q = q.limit(250).order_by(lo.c.login_name.asc()) db = d.connect() return db.execute(q)
def _notification_count(self, userid): return (d.connect().query(site.SavedNotification).filter( site.SavedNotification.userid == userid).count())
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 bulk_edit(userid, action, submissions=(), characters=(), journals=()): if not submissions and not characters and not journals or action == 'null': return 'Nothing to do.' if action == 'show': # Unhide (show/make visible) a submission def action(tbl): return (tbl.update().values( settings=sa.func.replace(tbl.c.settings, 'h', '')).where( tbl.c.settings.op('~')('h'))) action_string = 'unhidden' provide_link = True elif action == 'hide': # Hide a submission from public view def action(tbl): return (tbl.update().values( settings=tbl.c.settings.op('||')('h')).where( tbl.c.settings.op('!~')('h'))) action_string = 'hidden' # There's no value in giving the user a link to the submission as they # won't be able to see it. provide_link = False elif action.startswith('rate-'): # Re-rate a submission _, _, rating = action.partition('-') rating = int(rating) def action(tbl): return (tbl.update().values(rating=rating).where( tbl.c.rating != rating)) action_string = 'rerated ' + ratings.CODE_TO_NAME[rating] provide_link = True elif action == 'clearcritique': # Clear the "critique requested" flag def action(tbl): return (tbl.update().values( settings=sa.func.replace(tbl.c.settings, 'q', '')).where( tbl.c.settings.op('~')('q'))) action_string = 'unmarked as "critique requested"' provide_link = True elif action == 'setcritique': # Set the "critique requested" flag def action(tbl): return (tbl.update().values( settings=tbl.c.settings.op('||')('q')).where( tbl.c.settings.op('!~')('q'))) action_string = 'marked as "critique requested"' provide_link = True else: raise WeasylError('Unexpected') db = d.connect() affected = collections.defaultdict(list) copyable = [] for (tbl, col, title_col, urlpart), values in zip(_tables, [submissions, characters, journals]): if values: results = db.execute( action(tbl).where(tbl.c[col].in_(values)).returning( tbl.c[col], tbl.c[title_col], tbl.c.userid)) for thingid, title, ownerid in results: affected[ownerid].append('- ' + text.markdown_link( title, '/%s/%s?anyway=true' % (urlpart, thingid))) if provide_link: copyable.append( '- ' + text.markdown_link(title, '/%s/%s' % (urlpart, thingid))) else: copyable.append('- %s' % (title, )) now = arrow.utcnow() values = [] for target, target_affected in affected.iteritems(): staff_note = '## The following items were %s:\n\n%s' % ( action_string, '\n'.join(target_affected)) values.append({ 'userid': userid, 'target_user': target, 'unixtime': now, 'settings': 's', 'content': staff_note, }) if values: db.execute(d.meta.tables['comments'].insert().values(values)) return 'Affected items (%s): \n\n%s' % (action_string, '\n'.join(copyable))
def finduser(userid, form): form.userid = d.get_int(form.userid) # If we don't have any of these variables, nothing will be displayed. So fast-return an empty list. if not form.userid and not form.username and not form.email and not form.dateafter \ and not form.datebefore and not form.excludesuspended and not form.excludebanned \ and not form.excludeactive and not form.ipaddr: return [] lo = d.meta.tables['login'] sh = d.meta.tables['comments'] pr = d.meta.tables['profile'] sess = d.meta.tables['sessions'] q = d.sa.select([ lo.c.userid, lo.c.login_name, lo.c.email, (d.sa.select([d.sa.func.count()]) .select_from(sh) .where(sh.c.target_user == lo.c.userid) .where(sh.c.settings.op('~')('s'))).label('staff_notes'), lo.c.settings, lo.c.ip_address_at_signup, (d.sa.select([sess.c.ip_address]) .select_from(sess) .where(lo.c.userid == sess.c.userid) .limit(1) .order_by(sess.c.created_at.desc()) .correlate(sess) ).label('ip_address_session'), ]).select_from( lo.join(pr, lo.c.userid == pr.c.userid) .join(sess, sess.c.userid == pr.c.userid, isouter=True) ) # Is there a better way to only select unique accounts, when _also_ joining sessions? This _does_ work, though. q = q.distinct(lo.c.login_name) if form.userid: q = q.where(lo.c.userid == form.userid) elif form.username: q = q.where(lo.c.login_name.op('~')(form.username)) elif form.email: q = q.where(d.sa.or_( lo.c.email.op('~')(form.email), lo.c.email.op('ilike')('%%%s%%' % form.email), )) # Filter for banned and/or suspended accounts if form.excludeactive == "on": q = q.where(lo.c.settings.op('~')('[bs]')) if form.excludebanned == "on": q = q.where(lo.c.settings.op('!~')('b')) if form.excludesuspended == "on": q = q.where(lo.c.settings.op('!~')('s')) # Filter for IP address if form.ipaddr: q = q.where(d.sa.or_( lo.c.ip_address_at_signup.op('ilike')('%s%%' % form.ipaddr), sess.c.ip_address.op('ilike')('%s%%' % form.ipaddr) )) # Filter for date-time if form.dateafter and form.datebefore: q = q.where(d.sa.between(pr.c.unixtime, arrow.get(form.dateafter), arrow.get(form.datebefore))) elif form.dateafter: q = q.where(pr.c.unixtime >= arrow.get(form.dateafter)) elif form.datebefore: q = q.where(pr.c.unixtime <= arrow.get(form.datebefore)) # Apply any row offset if form.row_offset: q = q.offset(form.row_offset) q = q.limit(250).order_by(lo.c.login_name.asc()) db = d.connect() return db.execute(q)
def update_last_submission_time(userid, unixtime): profile = d.meta.tables['profile'] db = d.connect() db.execute(profile.update().where(profile.c.userid == userid).values( latest_submission_time=unixtime)) db.flush()
def create_friendship(user1, user2, settings=None): db = d.connect() db.add(users.Friendship(userid=user1, otherid=user2, settings=settings)) db.flush()
def add_entity(entity): db = d.connect() db.add(entity) db.flush() return entity
def create_blocktag(userid, tagid, rating): db = d.connect() db.add(content.Blocktag(userid=userid, tagid=tagid, rating=rating)) db.flush()
def insert(userid, submitid=None, charid=None, journalid=None, updateid=None, parentid=None, content=None): if not submitid and not charid and not journalid and not updateid: raise WeasylError("Unexpected") elif not content: raise WeasylError("commentInvalid") # Determine indent and parentuserid if parentid: if updateid: parentuserid = d.engine.scalar( "SELECT userid FROM siteupdatecomment WHERE commentid = %(parent)s", parent=parentid) if parentuserid is None: raise WeasylError("Unexpected") else: parent = d.engine.execute( "SELECT userid, indent FROM {table} WHERE commentid = %(parentid)s" .format(table="comments" if submitid else "charcomment" if charid else "journalcomment", ), parentid=parentid, ).first() if not parent: raise WeasylError("Unexpected") indent = parent.indent + 1 parentuserid = parent.userid elif updateid: parentid = None # parentid == 0 parentuserid = None else: indent = 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(), indent=indent).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, indent)" " VALUES (%(user)s, %(target)s, %(parent)s, %(content)s, %(now)s, %(indent)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(), indent=indent, ) # 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, 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) and not staffnotes): 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 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 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( os.path.join(MediaItem._base_file_path, submitfile['file_url'][1:])) 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]) sub_media = media.get_submission_media(submitid) 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 select_info(folderid): db = d.connect() f = d.meta.tables['folder'] q = sa.select([f.c.title, f.c.settings]).where(f.c.folderid == folderid) results = db.execute(q).fetchall() return results[0]
def edit(userid, submission, embedlink=None, friends_only=False, critique=False): query = d.engine.execute( "SELECT userid, subtype, settings FROM submission WHERE submitid = %(id)s", id=submission.submitid).first() if not query or "h" in query[2]: raise WeasylError("Unexpected") elif userid != query[0] and userid not in staff.MODS: raise WeasylError("InsufficientPermissions") elif not submission.title: raise WeasylError("titleInvalid") elif not submission.rating: raise WeasylError("Unexpected") elif not folder.check(query[0], submission.folderid): raise WeasylError("Unexpected") elif submission.subtype // 1000 != query[1] // 1000: raise WeasylError("Unexpected") elif 'v' in query[2] and not embed.check_valid(embedlink): raise WeasylError("embedlinkInvalid") elif 'D' in query[2]: check_google_doc_embed_data(embedlink) profile.check_user_rating_allowed(userid, submission.rating) # Assign settings settings = [query[2].replace("f", "").replace("q", "")] settings.append("f" if friends_only else "") settings.append("q" if critique else "") settings = "".join(settings) if "v" in settings: submission.content = "%s\n%s" % (embedlink, submission.content) if "f" in settings: welcome.submission_became_friends_only(submission.submitid, userid) # TODO(kailys): maintain ORM object db = d.connect() su = d.meta.tables['submission'] q = (su.update().values( folderid=submission.folderid, title=submission.title, content=submission.content, subtype=submission.subtype, rating=submission.rating, settings=settings, ).where(su.c.submitid == submission.submitid)) db.execute(q) if 'D' in settings: db = d.connect() gde = d.meta.tables['google_doc_embeds'] q = (gde.update().values(embed_url=embedlink).where( gde.c.submitid == submission.submitid)) db.execute(q) if userid != query[0]: from weasyl import moderation moderation.note_about( userid, query[0], 'The following submission was edited:', '- ' + text.markdown_link( submission.title, '/submission/%s?anyway=true' % (submission.submitid, )))
def is_hidden(submitid): db = d.connect() su = d.meta.tables['submission'] q = d.sa.select([su.c.settings.op('~')('h')]).where(su.c.submitid == submitid) results = db.execute(q).fetchall() return bool(results and results[0][0])
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 create_follow(user1, user2, settings='scftj'): db = d.connect() db.add(users.Follow(userid=user1, otherid=user2, settings=settings)) db.flush()
def setupdb(request): db = define.connect() db.execute('DROP SCHEMA public CASCADE') db.execute('CREATE SCHEMA public') db.execute('CREATE EXTENSION HSTORE') define.meta.create_all(define.engine)
def create_visual(userid, submission, friends_only, tags, imageURL, thumbfile, submitfile, critique, create_notifications): if imageURL: resp = _http_get_if_crosspostable(imageURL) submitfile = resp.content # Determine filesizes thumbsize = len(thumbfile) submitsize = len(submitfile) if not submitsize: raise WeasylError("submitSizeZero") elif thumbsize > 10 * _MEGABYTE: raise WeasylError("thumbSizeExceedsLimit") im = image.from_string(submitfile) submitextension = images.image_extension(im) if submitextension not in [".jpg", ".png", ".gif"]: raise WeasylError("submitType") if _limit(submitsize, submitextension): raise WeasylError("submitSizeExceedsLimit") submit_file_type = submitextension.lstrip('.') submit_media_item = orm.MediaItem.fetch_or_create( submitfile, file_type=submit_file_type, im=im) check_for_duplicate_media(userid, submit_media_item.mediaid) cover_media_item = submit_media_item.ensure_cover_image(im) # Thumbnail stuff. # Always create a 'generated' thumbnail from the source image. with BytesIO(submitfile) as buf: thumbnail_formats = images_new.get_thumbnail(buf) thumb_generated, thumb_generated_file_type, thumb_generated_attributes = thumbnail_formats.compatible thumb_generated_media_item = orm.MediaItem.fetch_or_create( thumb_generated, file_type=thumb_generated_file_type, attributes=thumb_generated_attributes, ) if thumbnail_formats.webp is None: thumb_generated_media_item_webp = None else: thumb_generated, thumb_generated_file_type, thumb_generated_attributes = thumbnail_formats.webp thumb_generated_media_item_webp = orm.MediaItem.fetch_or_create( thumb_generated, file_type=thumb_generated_file_type, attributes=thumb_generated_attributes, ) # If requested, also create a 'custom' thumbnail. thumb_media_item = media.make_cover_media_item(thumbfile) if thumb_media_item: thumb_custom = images.make_thumbnail(image.from_string(thumbfile)) thumb_custom_media_item = orm.MediaItem.fetch_or_create( thumb_custom.to_buffer(format=submit_file_type), file_type=submit_file_type, im=thumb_custom) # Assign settings settings = [] settings.append("f" if friends_only else "") settings.append("q" if critique else "") settings = "".join(settings) # TODO(kailys): maintain ORM object db = d.connect() now = arrow.get() q = (d.meta.tables['submission'].insert().values([{ "folderid": submission.folderid, "userid": userid, "unixtime": now, "title": submission.title, "content": submission.content, "subtype": submission.subtype, "rating": submission.rating.code, "settings": settings, "favorites": 0, "submitter_ip_address": submission.submitter_ip_address, "submitter_user_agent_id": submission.submitter_user_agent_id, }]).returning(d.meta.tables['submission'].c.submitid)) submitid = db.scalar(q) orm.SubmissionMediaLink.make_or_replace_link(submitid, 'submission', submit_media_item) orm.SubmissionMediaLink.make_or_replace_link(submitid, 'cover', cover_media_item) orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-generated', thumb_generated_media_item) if thumb_generated_media_item_webp is not None: orm.SubmissionMediaLink.make_or_replace_link( submitid, 'thumbnail-generated-webp', thumb_generated_media_item_webp) if thumb_media_item: orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-source', thumb_media_item) orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-custom', thumb_custom_media_item) # Assign search tags searchtag.associate(userid, tags, submitid=submitid) # Create notifications if create_notifications: _create_notifications(userid, submitid, submission.rating, settings) d.metric('increment', 'submissions') d.metric('increment', 'visualsubmissions') return submitid
def create_ignoreuser(ignorer, ignoree): db = d.connect() db.add(users.Ignorama(userid=ignorer, otherid=ignoree)) db.flush()
def create_literary(userid, submission, embedlink=None, friends_only=False, tags=None, coverfile=None, thumbfile=None, submitfile=None, critique=False, create_notifications=True): if embedlink: check_google_doc_embed_data(embedlink) # Determine filesizes coversize = len(coverfile) thumbsize = len(thumbfile) submitsize = len(submitfile) if not submitsize and not embedlink: raise WeasylError("submitSizeZero") elif coversize > 10 * _MEGABYTE: raise WeasylError("coverSizeExceedsLimit") elif thumbsize > 10 * _MEGABYTE: raise WeasylError("thumbSizeExceedsLimit") if submitsize: submitextension = files.get_extension_for_category( submitfile, m.TEXT_SUBMISSION_CATEGORY) if submitextension is None: raise WeasylError("submitType") if _limit(submitsize, submitextension): raise WeasylError("submitSizeExceedsLimit") submit_media_item = orm.MediaItem.fetch_or_create( submitfile, file_type=submitextension.lstrip('.')) check_for_duplicate_media(userid, submit_media_item.mediaid) else: submit_media_item = None thumb_media_item = media.make_cover_media_item(thumbfile) cover_media_item = media.make_cover_media_item(coverfile) if cover_media_item and not thumb_media_item: thumb_media_item = cover_media_item # Assign settings settings = [] settings.append("f" if friends_only else "") settings.append("q" if critique else "") if embedlink: settings.append('D') settings = "".join(settings) # Create submission # TODO(kailys): use ORM object db = d.connect() now = arrow.get() q = (d.meta.tables['submission'].insert().values([{ "folderid": submission.folderid, "userid": userid, "unixtime": now, "title": submission.title, "content": submission.content, "subtype": submission.subtype, "rating": submission.rating.code, "settings": settings, "favorites": 0, "submitter_ip_address": submission.submitter_ip_address, "submitter_user_agent_id": submission.submitter_user_agent_id, }]).returning(d.meta.tables['submission'].c.submitid)) submitid = db.scalar(q) if embedlink: q = (d.meta.tables['google_doc_embeds'].insert().values( submitid=submitid, embed_url=embedlink)) db.execute(q) # Assign search tags searchtag.associate(userid, tags, submitid=submitid) if submit_media_item: orm.SubmissionMediaLink.make_or_replace_link(submitid, 'submission', submit_media_item) if cover_media_item: orm.SubmissionMediaLink.make_or_replace_link(submitid, 'cover', cover_media_item) if thumb_media_item: orm.SubmissionMediaLink.make_or_replace_link(submitid, 'thumbnail-source', thumb_media_item) # Create notifications if create_notifications: _create_notifications(userid, submitid, submission.rating, settings) d.metric('increment', 'submissions') d.metric('increment', 'literarysubmissions') return submitid, bool(thumb_media_item)
def create_journal_tag(tagid, targetid, settings=None): db = d.connect() db.add( content.JournalTag(tagid=tagid, targetid=targetid, settings=settings)) db.flush()
def main(): db = connect() print 'starting media_items' port_media_items(db)