コード例 #1
0
def new_group(request, group_name, group_description):
    name = group_name
    description = group_description

    if models.Category.all_objects.get_or_none(name=name):
        raise ServiceError('A group already exists with that name.')
    if models.Category.objects.filter(
            founder=request.user).count() >= models.Category.FOUND_LIMIT:
        raise ServiceError(
            'Sorry, you can only found up to %s groups at this time.' %
            models.Category.FOUND_LIMIT)

    group = models.Category(
        founder=request.user,
        founded=time.time(),
        name=name,
        description=description,
    )

    problem = group.validate()
    if problem:
        raise ServiceError(problem)

    group.save()

    request.user.userinfo.details.force()

    # A founder auto-follows their group.
    models.FollowCategory(category=group, user=request.user).save()

    group_info = {'name': name, 'description': description}
    fact.record('new_group', request, group_info)
    Metrics.new_group.record(request, group_info=group_info)
コード例 #2
0
ファイル: models.py プロジェクト: eiritana/canvas
def complete_quest(user, quest_comment, access_token, request=None):
    if user.id != quest_comment.author_id:
        raise ServiceError("You can't share to your timeline a drawing you didn't create.")

    try:
        user.facebookuser
    except FacebookUser.DoesNotExist:
        raise ServiceError("Can't share to your timeline if you haven't added your Facebook account yet.")

    # Although we've renamed it to "draw", Facebook still internally refers to it as "complete".
    action = 'complete'

    quest_url = quest_comment.get_share_page_url_with_tracking(user, 'facebook', absolute=True)

    @bgwork.defer
    def rewards():
        economy.credit_personal_share(user)

    send_action = '{}:{}'.format(settings.FACEBOOK_NAMESPACE, action)

    @bgwork.defer
    def do_graph_action():
        try:
            graph = GraphAPI(access_token)
            graph.put_object('me', send_action, quest=quest_url)
            if request:
                Metrics.share_to_timeline.record(request, quest=quest_url)
        except GraphAPIError as e:
            if request:
                Metrics.share_to_timeline_error.record(request, quest=quest_url)
            client.create_from_exception()
コード例 #3
0
def set_playback_data(request):
    comment = None

    if 'uuid' not in request.POST:
        try:
            comment_id = request.POST['comment_id']
        except KeyError:
            raise ServiceError("Missing comment ID.")

        comment = get_object_or_404(QuestComment, id=comment_id)

        if request.user.id != comment.author.id:
            raise ServiceError("Can't upload playback data to a drawing you didn't create.")

    if 'playback_plist_data' in request.FILES:
        #plist = readPlistFromBytes(b''.join(request.FILES.get('playback_plist_data').chunks()))
        plist = readPlist(request.FILES['playback_plist_data'])
        playback_data = json.backend_dumps(plist)
    elif 'playback_data' in request.FILES:
        playback_data = u''.join(request.FILES.get('playback_data').chunks())
    else:
        playback_data = request.POST['playback_data']

    if comment is not None:
        save_playback_data(playback_data, comment=comment)
    else:
        save_playback_data(playback_data, uuid=request.POST['uuid'])
コード例 #4
0
ファイル: upload.py プロジェクト: eiritana/canvas
def combine_upload_chunks(request, chunks, metadata, is_quest=False):
    keys = ['chunk:{0}'.format(chunk) for chunk in chunks]

    raw_values = redis.mget(keys)

    if not all(raw_values):
        raise ServiceError("Missing uploaded chunk, please retry.")

    values = [b64decode(val) for val in raw_values]
    filedata = "".join(values)

    fs = get_fs(*settings.IMAGE_FS)
    remix_of = metadata.get('remix_of')
    stamps_used = metadata.get('used_stamps', []) or []
    text_used = metadata.get('used_text', '') or ''

    redis.delete(*keys)

    try:
        return create_content(request.META['REMOTE_ADDR'],
                              fs,
                              filedata,
                              remix_of,
                              stamps_used,
                              text_used,
                              '',
                              is_quest=is_quest)
    except IOError, e:
        raise ServiceError("Unable to read image.")
コード例 #5
0
ファイル: models.py プロジェクト: eiritana/canvas
    def invite(self, inviter, invitee, type='invite'):
        if invitee.id in self:
            raise ServiceError("User has already been invited.")
        if invitee.id == inviter.id:
            raise ServiceError(
                "You can't invite yourself to remix! You're already here!")
        if self.comment.author == invitee:
            raise ServiceError("That user is already in this thread.")
        self.actions[type](inviter, self.comment, invitee)
        self.sadd(invitee.id)

        invitee.remix_invites.add_invite(self.comment)
コード例 #6
0
def add_sticker_to_comment(request, comment_id, type_id, epic_message=None):
    """ Stickers a comment. You can be logged in or out. """
    sticker = stickers.get(int(type_id))
    comment = get_object_or_404(models.Comment, pk=comment_id)

    if epic_message and len(epic_message) > knobs.STICKER_MESSAGE_MAX_LENGTH:
        raise ServiceError("Message is too long.")
    elif epic_message and not (sticker.cost and sticker.cost >=
                               knobs.EPIC_STICKER_COST_THRESHOLD):
        raise ServiceError("Messages can only be attached to epic stickers.")

    # Calculate if this user has exceeded the stickering rate limit.
    prefix = 'user:%s:stick_limit:' % request.user.id
    if not RateLimit(prefix + 'h', 600, 60 * 60).allowed() or not RateLimit(
            prefix + 'd', 1000, 8 * 60 * 60).allowed():
        Metrics.sticker_ratelimit.record(request,
                                         sticker_type=sticker.type_id,
                                         comment=comment.id)
        raise ServiceError("Attempting to sticker too quickly.")

    prev_top_sticker = comment.details().top_sticker()
    remaining = _sticker_comment(request,
                                 comment,
                                 sticker.type_id,
                                 epic_message=epic_message)
    Metrics.sticker.record(request,
                           sticker_type=sticker.type_id,
                           comment=comment.id)

    comment_details = comment.details()
    top_sticker = comment_details.top_sticker()

    @bgwork.defer
    def update_stickered_users():
        get_most_stickered_unfollowed_users(request.user).force()

    if prev_top_sticker is None or prev_top_sticker['type_id'] != top_sticker:

        @bgwork.defer
        def update_footer():
            if comment.footer.should_exist():
                comment.footer.call_update_in_new_process()

    return {
        'new_counts': comment_details.sticker_counts,
        'sorted_counts': comment_details.sorted_sticker_counts(),
        'top_sticker': top_sticker,
        'remaining': remaining,
    }
コード例 #7
0
ファイル: url_util.py プロジェクト: eiritana/canvas
def verify_first_party_url(url):
    """
    Also allows iTunes store URLs.
    """
    if not url or not url.startswith('/'):
        parsed_url = urlparse.urlparse(url)

        try:
            protocol = parsed_url[0]
            domain = parsed_url[1]
        except IndexError:
            raise ServiceError("Invalid share url.")

        if protocol not in ['http', 'https'] or domain not in  ['itunes.apple.com', 'example.com']:
            # Only 1st party redirects, to avoid security holes that 3rd party redirects imply
            raise ServiceError("Invalid share url.")
コード例 #8
0
ファイル: views.py プロジェクト: eiritana/canvas
def groups(request, payload={}, group_name=None):
    """
    Groups endpoint of the example.com public api

    Request with an id parameter:
        /public_api/groups/funny

    POST JSON in the following format:
        POST /public_api/groups/
        {"ids":["funny","canvas"]}

    Group posts will be returned """ + str(knobs.PUBLIC_API_PAGINATION_SIZE) + """ at a time, ordered newest to oldest. You can request posts beyond the initial range by POSTing JSON in the following format:

        POST /public_api/groups/
        {"ids":[{"group":"funny","skip":100},"canvas"}
    """
    Metrics.api_group.record(request)
    ids = payload.get('ids')

    if group_name and not ids:
        try:
            return PublicAPIGroupDetails(group_name).to_client()
        except (ObjectDoesNotExist, Http404):
            raise ServiceError("Group does not exist")

    elif ids:
        def inner_group(group_arg):
            try:
                return PublicAPIGroupDetails(group_arg).to_client()
            except (ObjectDoesNotExist, Http404):
                return None

        potential_groups = [inner_group(x) for x in payload.get('ids')]
        return {'groups': [x for x in potential_groups if x]}
コード例 #9
0
ファイル: views.py プロジェクト: eiritana/canvas
def users(request, payload={}, username=None):
    """
    Users endpoint of the example.com public api

    Request with an id parameter:

        /public_api/users/watermelonbot

    POST JSON in the following format:

        POST /public_api/users/
        {"ids":["watermelonbot", "jeff"]}

    User posts will be returned """ + str(knobs.PUBLIC_API_PAGINATION_SIZE) + """ at a time, ordered newest to oldest. You can request posts beyond the initial range by POSTing JSON in the following format:

        POST /public_api/users/
        {"ids":[{"user":"******","skip":100},"jeff"}
    """
    Metrics.api_user.record(request)
    if username and not payload:
        try:
            return PublicAPIUserDetails(username).to_client()
        except (ObjectDoesNotExist, Http404):
            raise ServiceError("User does not exist")

    elif payload:
        def inner_user(user_arg):
            try:
                return PublicAPIUserDetails(user_arg).to_client()
            except (ObjectDoesNotExist, Http404):
                return None

        potential_users = [inner_user(x) for x in payload.get('ids')]
        return {'users': [x for x in potential_users if x]}
コード例 #10
0
ファイル: views.py プロジェクト: eiritana/canvas
def posts(request, payload={}, short_id=None):
    """
    Posts endpoint of the example.com public api

    Request with an id parameter:

        /public_api/posts/1qkx8

    POST JSON in the following format:

        POST /public_api/posts/
        {"ids":["1qkx8","ma6fz"]}
    """
    Metrics.api_comment.record(request)
    ids = payload.get('ids')

    if short_id and not ids:
        try:
            comment = Comment.details_by_id(long_id(short_id), promoter=PublicAPICommentDetails)
            (comment,) = CachedCall.multicall([comment])
            return comment.to_client()
        except (ObjectDoesNotExist, util.Base36DecodeException):
            raise ServiceError("Post not found")

    elif ids:
        ids = [long_id(x) for x in set(ids)]
        calls = [Comment.details_by_id(id, ignore_not_found=True, promoter=PublicAPICommentDetails) for id in ids]
        comments = CachedCall.multicall(calls, skip_decorator=True)
        return {'posts': [x.to_client() for x in comments if x]}
コード例 #11
0
ファイル: upload.py プロジェクト: eiritana/canvas
def api_upload(request):
    content_type = request.META['CONTENT_TYPE']

    url = ''
    filedata = None

    if 'file' in request.FILES:
        filedata = "".join(request.FILES['file'].chunks())

    elif content_type.startswith('application/json'):
        json = util.loads(request.raw_post_data)
        url = json.get('url', '').strip()
        filedata = _fetch_url(url)

    elif content_type.startswith('application/base64-png'):
        token = 'data:image/png;base64,'
        header, data = request.raw_post_data.split(',', 2)
        if not header.startswith(
                'data:'
        ) or not 'image/png' in header or not 'base64' in header:
            return {
                'success': False,
                'code': 'unknown',
                'reason': 'Missing data url header.'
            }
        else:
            filedata = b64decode(data)

    if filedata:
        return _got_imagedata(filedata, request, url=url)
    else:
        raise ServiceError("No file or url.")
コード例 #12
0
def search_stamps(request, query, start):
    """
    Searches the special "stamps" group for stamps that match the search query.

    Returns {comments: [list of comment details]}
    """
    qs = query
    try:
        start = int(start)
    except TypeError:
        raise ServiceError('Invalid "start" parameter.')

    stamps = models.Category.objects.get(name="stamps")
    if qs:
        ids = [
            x for x in models.Comment.objects.filter(
                category=stamps).filter(Q(
                    title__icontains=qs)).values_list('id', flat=True)
        ]
        ids = ids[start:start + 32]
        comments = models.Comment.curated.exclude(
            reply_content__id=None).in_bulk(ids)
        details = CachedCall.multicall(
            [comments[id].details for id in ids if id in comments])

    else:
        comments = models.Comment.curated.filter(category=stamps).exclude(
            reply_content__id=None).order_by('-id')
        comments = comments[start:start + 32]
        details = CachedCall.queryset_details(comments)

    return {'comments': details}
コード例 #13
0
def api_upload(request):
    content_type = request.META.get('CONTENT_TYPE', '')

    url = ''
    filedata = None

    if 'file' in request.FILES:
        filedata = "".join(request.FILES['file'].chunks())

    elif content_type.startswith('application/json'):
        json = util.loads(request.body)
        url = json.get('url', '').strip()
        filedata = _fetch_url(url)

    elif content_type.startswith('application/base64-png'):
        token = 'data:image/png;base64,'
        header, data = request.body.split(',', 2)
        if not header.startswith('data:') or not 'image/png' in header or not 'base64' in header:
            return {'success': False, 'code': 'unknown', 'reason': 'Missing data url header.'}
        else:
            filedata = b64decode(data)

    if filedata:
        ret = _got_imagedata(filedata, request, url=url)
        from canvas.cache_patterns import cache
        from canvas.models import Content
        util.papertrail.debug('UPLOADS: _got_imagedata, actual cache is {} for content ID {}'.format(cache.get('content:%s:full_details_v26' % ret['content']['id']), ret['content']['id']))
        util.papertrail.debug('UPLOADS: _got_imagedata, actual content object for ID {} exists: {}'.format(ret['content']['id'], Content.all_objects.filter(id=ret['content']['id']).exists()))
        util.papertrail.debug('UPLOADS: _got_imagedata: {} {}'.format(ret.get('success'), ret['content']['id']))
        return ret
    else:
        raise ServiceError("No file or url.")
コード例 #14
0
def update_comment_tags(request, comment_id, tags):
    comment = get_object_or_404(models.Comment, pk=comment_id)
    if request.user.is_staff or request.user == comment.author:
        new_tags = set(tags)
        original = comment.tags.smembers()

        # tags to remove
        for t in (original - new_tags):
            Tag(t).untag_comment(comment)
            comment.tags.srem(t)

        # tags to add
        for t in (new_tags - original):
            Tag(t).tag_comment(comment)
            comment.tags.sadd(t)

        comment.details.force()
        ret_tags = []
        for t in comment.tags.smembers():
            ret_tags += [{
                'name': t,
                'url': Tag(t).get_absolute_url(),
            }]

        return {'tags': ret_tags}

    else:
        raise ServiceError("Permission denied")
コード例 #15
0
ファイル: api.py プロジェクト: eiritana/canvas
def flag_comment(request, comment_id):
    comment = get_object_or_404(models.Comment, pk=comment_id)

    # If the user hits the flag rate limit, silently ignore it.
    prefix = 'user:%s:flag_limit:' % request.user.id

    def allowed(key, val):
        if request.user.is_staff:
            return True
        freq, timespan = val
        return RateLimit(prefix+key, freq, timespan).allowed()

    if not all(allowed(key, val) for key,val in knobs.FLAG_RATE_LIMITS.iteritems()):
        Metrics.flag_ratelimit.record(request, comment=comment.id)
        raise ServiceError('Flag rate limit exceeded.')
    else:
        flag = comment.add_flag(request.user, ip=request.META['REMOTE_ADDR'])

        if flag is not None:
            if settings.PROJECT == 'canvas':
                request.user.redis.hidden_comments.hide_comment(comment)

            Metrics.flag.record(request, comment=comment.id)

            if comment.anonymous:
                Metrics.flag_anonymous_post.record(request, comment=comment.id)

    if settings.AUTO_MODERATE_FLAGGED_COMMENTS_THRESHOLD is not None:
        if (not comment.judged
                and comment.details().flag_counts[0] == settings.AUTO_MODERATE_FLAGGED_COMMENTS_THRESHOLD):
            comment.visibility = models.Visibility.DISABLED
            comment.save()
            comment.visibility_changed()

    return {'flag_counts': comment.details().flag_counts, 'flag_id': flag.id}
コード例 #16
0
def associate_facebook_account(user, facebook_access_token, request=None):
    try:
        fb_user = FacebookUser.get_or_create_from_access_token(
            facebook_access_token, request=request)
    except GraphAPIError as e:
        papertrail.debug(
            u'GraphAPIError inside associate_facebook_account: {} (user: {}, token: {})'
            .format(e.message, user.username, facebook_access_token))

        raise ServiceError(
            _("There appears to be an issue communicating with Facebook. Please try again."
              ))

    try:
        existing_fb_user = user.facebookuser

        if existing_fb_user.fb_uid == fb_user.fb_uid:
            return

        existing_fb_user.user = None
        existing_fb_user.save()
    except FacebookUser.DoesNotExist:
        pass

    fb_user.user = user
    fb_user.save()

    @bgwork.defer
    def notify_friends():
        fb_user.notify_friends_of_signup(facebook_access_token)
        fb_user.respond_to_apprequest_invites(facebook_access_token)
コード例 #17
0
ファイル: api.py プロジェクト: eiritana/canvas
def tumblr_post_photo(request, access_token, access_token_secret, blog_hostname, comment_id):
    comment = get_object_or_404(QuestComment, id=comment_id)

    try:
        models.post_photo(request.user, blog_hostname, comment)
    except requests.exceptions.HTTPError as e:
        client.create_from_exception()
        raise ServiceError("Error posting to Tumblr.")
コード例 #18
0
def JSONPResponse(request, response, **kwargs):
    callback = request.GET.get('callback', 'callback')
    if not callback.replace('_', '').isalnum():
        raise ServiceError()
    return HttpResponse(
        '%s(%s);' % (callback, client_dumps(response, viewer=request.user)),
        mimetype='application/javascript',
        **kwargs)
コード例 #19
0
ファイル: api.py プロジェクト: eiritana/canvas
def claim_comment(request, comment_id):
    comment = models.Comment.all_objects.get(id=comment_id)

    if not comment.author == request.user:
        raise ServiceError("Not comment author")

    comment.make_non_anonymous(request.user)

    Metrics.claim_post.record(request, comment=comment.id)
コード例 #20
0
ファイル: models.py プロジェクト: eiritana/canvas
 def check_canvas_account_password(self, username, password):
     try:
         ret = _canvas_api('/user/check_password', {'username': username, 'password': password})
     except (urllib2.URLError, socket.timeout, httplib.BadStatusLine,):
         raise ServiceError("Couldn't migrate Canvas account to DrawQuest. Please try again later.")
     if not ret['success']:
         raise Exception("Couldn't migrate Canvas account to DrawQuest.")
     del ret['success']
     return ret['correct']
コード例 #21
0
def _fetch_url(url):
    if not url.startswith('http://') and not url.startswith('https://'):
        if '://' not in url:
            url = 'http://' + url
        else:
            # Must at least prevent file://, better to whitelist than blacklist
            raise ServiceError("Only http/https links allowed")

    try:
        url_request = urllib2.Request(url)
        url_response = urllib2.urlopen(url_request)
    except (IOError, httplib.HTTPException, UnicodeEncodeError):
        raise ServiceError("Unable to download image.")

    if url_response.getcode() != 200:
        raise ServiceError("The requested image could not be downloaded. Please try a different image.")
    else:
        return url_response.read()
コード例 #22
0
def staff_gift_coins(request, username, amount):
    amount = int(amount)

    if amount > 400:
        raise ServiceError(
            "Can't gift more than 400 at a time (sanity check).")

    user = get_object_or_404(User, username=username)
    economy.credit(user, amount)
コード例 #23
0
ファイル: api.py プロジェクト: eiritana/canvas
def script_share(request, s3sum):
    if not s3sum.isalnum():
        raise ServiceError('sums must be alphanumeric')

    remixplugin = models.RemixPlugin(author=request.user,
                                     timestamp=Now(),
                                     s3md5=s3sum)
    remixplugin.save()
    return {'plugin_url': remixplugin.get_url()}
コード例 #24
0
ファイル: models.py プロジェクト: eiritana/canvas
 def canvas_account_info(self, username):
     try:
         ret = _canvas_api('/user/info_for_drawquest_migration', {'username': username})
     except (urllib2.URLError, socket.timeout, httplib.BadStatusLine,):
         raise ServiceError("Couldn't migrate Canvas account to DrawQuest. Please try again later.")
     if not ret['success']:
         raise Exception("Couldn't migrate Canvas account to DrawQuest.")
     del ret['success']
     return ret
コード例 #25
0
ファイル: api.py プロジェクト: eiritana/canvas
def delete_comment(request, comment_id):
    comment = models.Comment.all_objects.get(id=comment_id)

    if not comment.author == request.user:
        raise ServiceError("Not comment author")

    comment.moderate_and_save(models.Visibility.UNPUBLISHED, request.user, undoing=True)

    Metrics.delete_post.record(request, comment=comment.id)
コード例 #26
0
    def invite(self, inviter, invitees, type='invite', ignore_errors=False):
        if not ignore_errors:
            for invitee in invitees:
                if invitee.id in self:
                    raise ServiceError(
                        "User {} has already been invited.".format(
                            invitee.username))
                if invitee.id == inviter.id:
                    raise ServiceError(
                        "You can't invite yourself - you're already here!")
                if self.quest.author == invitee:
                    raise ServiceError("That user is already in this quest.")

        self.sadd([invitee.id for invitee in invitees])

        for invitee in invitees:
            Actions.invite_user(inviter, self.quest, invitee)
            invitee.redis.quest_invites.add_invite(self.quest)
コード例 #27
0
ファイル: api.py プロジェクト: eiritana/canvas
def post_quest_comment(request,
                       quest_id,
                       content_id,
                       fact_metadata={},
                       facebook_share=False,
                       facebook_access_token=None):
    # Rate-limit?
    if not request.user.is_staff:
        prefix = 'user:{}:post_limit:'.format(request.user.id)
        if not RateLimit(prefix + 'h', 60, 60 * 60).allowed() or not RateLimit(
                prefix + 'd', 100, 8 * 60 * 60).allowed():
            raise ServiceError("Attempting to post drawings too quickly.")

    _, parent_comment, content, _, _, _ = validate_and_clean_comment(
        request.user,
        parent_comment=quest_id,
        reply_content=content_id,
    )

    if facebook_share:
        if not facebook_access_token:
            raise ServiceError(
                "Can't share to your timeline if you haven't signed into Facebook yet."
            )

        associate_facebook_account(request.user, facebook_access_token)

    comment = QuestComment.create_and_post(request,
                                           request.user,
                                           content,
                                           parent_comment,
                                           fact_metadata=fact_metadata)

    if facebook_share:
        complete_quest(request.user,
                       comment,
                       facebook_access_token,
                       request=request)

    return {
        'comment': comment.details(),
        'balance': economy.balance(request.user),
    }
コード例 #28
0
def metric_record(request, name, info={}):
    info = dict((str(key), value) for (key, value) in info.items())

    metric = Metrics.all.get(name)
    if not metric:
        raise ServiceError("Invalid metric name")

    if metric.ignore_from_api:
        return

    metric.record(request, **info)
コード例 #29
0
def set_playback_data(request):
    try:
        comment_id = request.POST['comment_id']
    except KeyError:
        raise ServiceError("Missing comment ID.")

    comment = get_object_or_404(QuestComment, id=comment_id)

    playback_data = request.POST['playback_data']

    PlaybackData.create_with_json(comment, playback_data)
コード例 #30
0
def check_star_rate_limit(request, comment):
    from drawquest.apps.stars.models import get_star_sticker
    # Calculate if this user has exceeded the stickering rate limit.
    prefix = 'user:{}:stick_limit:'.format(request.user.id)
    if not RateLimit(prefix + 'h', 500, 60 * 60).allowed() or not RateLimit(
            prefix + 'd', 1000, 8 * 60 * 60).allowed():
        Metrics.sticker_ratelimit.record(
            request,
            sticker_type=get_star_sticker().type_id,
            comment=comment.id)
        raise ServiceError("Attempting to star too quickly.")
コード例 #31
0
ファイル: api.py プロジェクト: StetHD/canvas-2
 def __init__(self):
     ServiceError.__init__(self, "No such notification.")