Beispiel #1
0
def manage_thumbnail_get_(request):
    form = request.web_input(submitid="", charid="", auto="")
    submitid = define.get_int(form.submitid)
    charid = define.get_int(form.charid)

    if submitid and request.userid not in staff.ADMINS and request.userid != define.get_ownerid(submitid=submitid):
        return Response(define.errorpage(request.userid, errorcode.permissions))
    elif charid and request.userid not in staff.ADMINS and request.userid != define.get_ownerid(charid=charid):
        return Response(define.errorpage(request.userid, errorcode.permissions))
    elif not submitid and not charid:
        return Response(define.errorpage(request.userid))

    if charid:
        source_path = define.url_make(charid, "char/.thumb", root=True)
        if os.path.exists(source_path):
            source = define.url_make(charid, "char/.thumb")
        else:
            source = define.url_make(charid, "char/cover")
    else:
        try:
            source = thumbnail.thumbnail_source(submitid)['display_url']
        except WeasylError:
            source = None

    return Response(define.webpage(request.userid, "manage/thumbnail.html", [
        # Feature
        "submit" if submitid else "char",
        # Targetid
        define.get_targetid(submitid, charid),
        # Thumbnail
        source,
        # Exists
        bool(source),
    ], options=['imageselect'], title="Select Thumbnail"))
Beispiel #2
0
def select(userid, submitid=None, charid=None, journalid=None):
    result = []

    if submitid:
        statement = ["""
            SELECT
                cm.commentid, cm.parentid, cm.userid, pr.username, lo.settings,
                cm.content, cm.unixtime, cm.settings, cm.indent, pr.config,
                cm.hidden_by
            FROM comments cm
            INNER JOIN profile pr USING (userid)
            INNER JOIN login lo USING (userid)
            WHERE cm.target_sub = %d
        """ % (submitid,)]
    else:
        statement = ["""
            SELECT
                cm.commentid, cm.parentid, cm.userid, pr.username, lo.settings, cm.content, cm.unixtime, cm.settings,
                cm.indent, pr.config, cm.hidden_by
            FROM %scomment cm
                INNER JOIN profile pr USING (userid)
                INNER JOIN login lo USING (userid)
            WHERE cm.targetid = %i
        """ % ("submit" if submitid else "char" if charid else "journal", d.get_targetid(submitid, charid, journalid))]

    # moderators get to view hidden comments
    if userid not in staff.MODS:
        statement.append(" AND cm.settings !~ 'h'")

    if userid:
        statement.append(m.MACRO_IGNOREUSER % (userid, "cm"))

    statement.append(" ORDER BY COALESCE(cm.parentid, 0), cm.unixtime")
    query = d.execute("".join(statement))

    for i, comment in enumerate(query):
        if comment[1]:
            break

        result.append({
            "commentid": comment[0],
            "parentid": comment[1],
            "userid": comment[2],
            "username": comment[3],
            "status": "".join({"b", "s"} & set(comment[4])),
            "content": comment[5],
            "unixtime": comment[6],
            "settings": comment[7],
            "indent": comment[8],
            "hidden": 'h' in comment[7],
            "hidden_by": comment[10],
        })

        _thread(query, result, i)

    media.populate_with_user_media(result)
    return result
Beispiel #3
0
def select(userid, submitid=None, charid=None, journalid=None, updateid=None):
    is_hidden = "cm.settings ~ 'h'"

    if submitid:
        statement = [
            """
            SELECT
                cm.commentid, cm.parentid, cm.userid, pr.username,
                cm.content, cm.unixtime, cm.settings ~ 'h', cm.hidden_by
            FROM comments cm
                INNER JOIN profile pr USING (userid)
            WHERE cm.target_sub = %d
        """ % (submitid, )
        ]
    else:
        unixtime = "cm.unixtime"

        if charid:
            table = "charcomment"
        elif journalid:
            table = "journalcomment"
        elif updateid:
            table = "siteupdatecomment"
            is_hidden = "cm.hidden_at IS NOT NULL"
            unixtime = "EXTRACT(EPOCH FROM cm.created_at)::int8 + %i" % (
                UNIXTIME_OFFSET, )
        else:
            raise WeasylError("Unexpected")

        statement = [
            """
            SELECT
                cm.commentid, cm.parentid, cm.userid, pr.username,
                cm.content, %s, %s, cm.hidden_by
            FROM %s cm
                INNER JOIN profile pr USING (userid)
            WHERE cm.targetid = %i
        """ % (unixtime, is_hidden, table,
               d.get_targetid(submitid, charid, journalid, updateid))
        ]

    # moderators get to view hidden comments
    if userid not in staff.MODS:
        statement.append(" AND NOT (%s)" % (is_hidden, ))

    if userid:
        statement.append(m.MACRO_IGNOREUSER % (userid, "cm"))

    statement.append(" ORDER BY cm.commentid")
    query = d.execute("".join(statement))
    result = thread(query, reverse_top_level=False)
    media.populate_with_user_media(result)
    return result
Beispiel #4
0
def remove(userid, submitid=None, charid=None, journalid=None):
    d.execute(
        "DELETE FROM favorite WHERE (userid, targetid, type) = (%i, %i, '%s')",
        [
            userid,
            d.get_targetid(submitid, charid, journalid),
            "s" if submitid else "f" if charid else "j"
        ])

    welcome.favorite_remove(userid,
                            submitid=submitid,
                            charid=charid,
                            journalid=journalid)
Beispiel #5
0
def check(userid, submitid=None, charid=None, journalid=None):
    if not userid:
        return False

    return d.execute(
        """
            SELECT EXISTS (
                SELECT 0 FROM favorite
                    WHERE (userid, targetid, type) = (%i, %i, '%s')
            )
        """, [
            userid, d.get_targetid(submitid, charid, journalid),
            "s" if submitid else "f" if charid else "j"
        ], options="bool")
Beispiel #6
0
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)
Beispiel #7
0
def select(userid, submitid=None, charid=None, journalid=None, updateid=None):
    is_hidden = "cm.settings ~ 'h'"

    if submitid:
        statement = ["""
            SELECT
                cm.commentid, cm.parentid, cm.userid, pr.username,
                cm.content, cm.unixtime, cm.settings ~ 'h', cm.hidden_by
            FROM comments cm
                INNER JOIN profile pr USING (userid)
            WHERE cm.target_sub = %d
        """ % (submitid,)]
    else:
        unixtime = "cm.unixtime"

        if submitid:
            table = "submitcomment"
        elif charid:
            table = "charcomment"
        elif journalid:
            table = "journalcomment"
        elif updateid:
            table = "siteupdatecomment"
            is_hidden = "cm.hidden_at IS NOT NULL"
            unixtime = "EXTRACT(EPOCH FROM cm.created_at)::int8 + %i" % (UNIXTIME_OFFSET,)
        else:
            raise WeasylError("Unexpected")

        statement = ["""
            SELECT
                cm.commentid, cm.parentid, cm.userid, pr.username,
                cm.content, %s, %s, cm.hidden_by
            FROM %s cm
                INNER JOIN profile pr USING (userid)
            WHERE cm.targetid = %i
        """ % (unixtime, is_hidden, table, d.get_targetid(submitid, charid, journalid, updateid))]

    # moderators get to view hidden comments
    if userid not in staff.MODS:
        statement.append(" AND NOT (%s)" % (is_hidden,))

    if userid:
        statement.append(m.MACRO_IGNOREUSER % (userid, "cm"))

    statement.append(" ORDER BY cm.commentid")
    query = d.execute("".join(statement))
    result = thread(query, reverse_top_level=False)
    media.populate_with_user_media(result)
    return result
Beispiel #8
0
def check(userid, submitid=None, charid=None, journalid=None):
    if not userid:
        return False

    return d.execute("""
            SELECT EXISTS (
                SELECT 0 FROM favorite
                    WHERE (userid, targetid, type) = (%i, %i, '%s')
            )
        """, [
        userid,
        d.get_targetid(submitid, charid, journalid),
        "s" if submitid else "f" if charid else "j"
    ],
                     options="bool")
Beispiel #9
0
def check(userid, submitid=None, charid=None, journalid=None):
    if not userid:
        return False

    return d.engine.scalar(
        """
            SELECT EXISTS (
                SELECT 0 FROM favorite
                    WHERE (userid, targetid, type) = (%(user)s, %(target)s, %(type)s)
            )
        """,
        user=userid,
        target=d.get_targetid(submitid, charid, journalid),
        type="s" if submitid else "f" if charid else "j",
    )
Beispiel #10
0
def create_favorite(userid, **kwargs):
    unixtime = kwargs.pop('unixtime', None)
    favorite.insert(userid, **kwargs)

    if unixtime is not None:
        if 'submitid' in kwargs:
            type_ = 's'
        elif 'charid' in kwargs:
            type_ = 'c'
        elif 'journalid' in kwargs:
            type_ = 'j'

        targetid = d.get_targetid(*kwargs.values())

        fav = content.Favorite.query.filter_by(userid=userid, type=type_, targetid=targetid).one()
        fav.unixtime = unixtime
        content.Favorite.dbsession.flush()
Beispiel #11
0
def manage_thumbnail_get_(request):
    form = request.web_input(submitid="", charid="", auto="")
    submitid = define.get_int(form.submitid)
    charid = define.get_int(form.charid)

    if submitid and request.userid not in staff.ADMINS and request.userid != define.get_ownerid(
            submitid=submitid):
        return Response(define.errorpage(request.userid,
                                         errorcode.permissions))
    elif charid and request.userid not in staff.ADMINS and request.userid != define.get_ownerid(
            charid=charid):
        return Response(define.errorpage(request.userid,
                                         errorcode.permissions))
    elif not submitid and not charid:
        return Response(define.errorpage(request.userid))

    if charid:
        source_path = define.url_make(charid, "char/.thumb", root=True)
        if os.path.exists(source_path):
            source = define.url_make(charid, "char/.thumb")
        else:
            source = define.url_make(charid, "char/cover")
    else:
        try:
            source = thumbnail.thumbnail_source(submitid)['display_url']
        except WeasylError:
            source = None

    return Response(
        define.webpage(
            request.userid,
            "manage/thumbnail.html",
            [
                # Feature
                "submit" if submitid else "char",
                # Targetid
                define.get_targetid(submitid, charid),
                # Thumbnail
                source,
                # Exists
                bool(source),
            ],
            options=['imageselect'],
            title="Select Thumbnail"))
Beispiel #12
0
    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)
Beispiel #13
0
    def remove_transaction(db):
        delete_result = db.execute(
            "DELETE FROM favorite WHERE (userid, targetid, type) = (%(user)s, %(target)s, %(type)s)",
            user=userid,
            target=d.get_targetid(submitid, charid, journalid),
            type="s" if submitid else "f" if charid else "j",
        )

        if delete_result.rowcount == 0:
            return

        if submitid:
            db.execute(
                "UPDATE submission SET favorites = favorites - 1 WHERE submitid = %(target)s",
                target=submitid,
            )

        welcome.favorite_remove(db,
                                userid,
                                submitid=submitid,
                                charid=charid,
                                journalid=journalid)
Beispiel #14
0
def select(userid, submitid=None, charid=None, journalid=None):
    result = []

    if submitid:
        statement = [
            """
            SELECT
                cm.commentid, cm.parentid, cm.userid, pr.username, lo.settings,
                cm.content, cm.unixtime, cm.settings, cm.indent, pr.config,
                cm.hidden_by
            FROM comments cm
            INNER JOIN profile pr USING (userid)
            INNER JOIN login lo USING (userid)
            WHERE cm.target_sub = %d
        """ % (submitid, )
        ]
    else:
        statement = [
            """
            SELECT
                cm.commentid, cm.parentid, cm.userid, pr.username, lo.settings, cm.content, cm.unixtime, cm.settings,
                cm.indent, pr.config, cm.hidden_by
            FROM %scomment cm
                INNER JOIN profile pr USING (userid)
                INNER JOIN login lo USING (userid)
            WHERE cm.targetid = %i
        """ % ("submit" if submitid else "char" if charid else "journal",
               d.get_targetid(submitid, charid, journalid))
        ]

    # moderators get to view hidden comments
    if userid not in staff.MODS:
        statement.append(" AND cm.settings !~ 'h'")

    if userid:
        statement.append(m.MACRO_IGNOREUSER % (userid, "cm"))

    statement.append(" ORDER BY COALESCE(cm.parentid, 0), cm.unixtime")
    query = d.execute("".join(statement))

    for i, comment in enumerate(query):
        if comment[1]:
            break

        result.append({
            "commentid": comment[0],
            "parentid": comment[1],
            "userid": comment[2],
            "username": comment[3],
            "status": "".join({"b", "s"} & set(comment[4])),
            "content": comment[5],
            "unixtime": comment[6],
            "settings": comment[7],
            "indent": comment[8],
            "hidden": 'h' in comment[7],
            "hidden_by": comment[10],
        })

        _thread(query, result, i)

    media.populate_with_user_media(result)
    return result
Beispiel #15
0
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
Beispiel #16
0
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
Beispiel #17
0
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)))
Beispiel #18
0
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
Beispiel #19
0
def remove(userid, submitid=None, charid=None, journalid=None):
    d.execute("DELETE FROM favorite WHERE (userid, targetid, type) = (%i, %i, '%s')",
              [userid, d.get_targetid(submitid, charid, journalid), "s" if submitid else "f" if charid else "j"])

    welcome.favorite_remove(userid, submitid=submitid, charid=charid, journalid=journalid)
Beispiel #20
0
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
Beispiel #21
0
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
Beispiel #22
0
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)))