Beispiel #1
0
    def get_profile(self):
        """Retrieve the Django UserProfile for this Person.

        This is full of hacks because all the Mozillians servers are throwing
        ObjectDoesNotExist errors (even in production) if we try a straight-up
        `User.objects.get(email=self.username)`. This method now exhaustively
        tries to get a User object from the database. If it doesn't find one,
        or finds one without a UserProfile, we make one on the spot, trying
        our best to fill things in sanely. FML.

        See: https://bugzilla.mozilla.org/show_bug.cgi?id=698699

        TODO: Remove this as soon as possible. It's insane.
        """
        user = (User.objects.filter(
            Q(email=self.username) | Q(username=self.username)))[:1]

        if user:
            # Yes, sometimes the User exists but the UserProfile doesn't.
            # See: https://bugzilla.mozilla.org/show_bug.cgi?id=699234
            try:
                profile = user[0].get_profile()
            except ObjectDoesNotExist, e:
                statsd.incr('user.errors.profile_doesnotexist')
                log.warning(e)

                profile = UserProfile.objects.create(user=user[0])
Beispiel #2
0
def submit_ticket(email, category, subject, body):
    """Submit a marketplace ticket to Zendesk.

    :arg email: user's email address
    :arg category: issue's category
    :arg subject: issue's subject
    :arg body: issue's description
    """
    # Create the Zendesk connection client.
    zendesk = get_zendesk()

    # Create the ticket
    new_ticket = {
        'ticket': {
            'requester_email': email,
            'subject': settings.ZENDESK_SUBJECT_PREFIX + subject,
            'description': body,
            'set_tags': category,
        }
    }
    try:
        ticket_url = zendesk.create_ticket(data=new_ticket)
        statsd.incr('questions.zendesk.success')
    except ZendeskError as e:
        log.error('Zendesk error: %s' % e.msg)
        statsd.incr('questions.zendesk.error')
        raise

    return ticket_url
Beispiel #3
0
    def get_by_unique_id(self, unique_id, use_master=False):
        """Retrieves a person from LDAP with this unique_id.

        Raises NO_SUCH_PERSON if unable to find them.

        use_master can be set to True to force reading from master
        where stale data isn't acceptable.
        """
        f = "(&(objectClass=mozilliansPerson)(uniqueIdentifier=%s))"
        q = filter_format(f, (unique_id, ))
        results = self._people_search(q, use_master)
        msg = 'Unable to locate %s in the LDAP directory'
        if not results:
            raise NO_SUCH_PERSON(msg % unique_id)
        elif len(results) == 1:
            _dn, attrs = results[0]
            # Pending users will detect the existance of another
            # person, but there won't be any data besides uniqueIdentifier
            if 'sn' not in attrs:
                raise NO_SUCH_PERSON(msg % unique_id)
            else:
                return Person.new_from_directory(attrs)
        else:
            msg = 'Multiple people found for %s. This should never happen.'
            statsd.incr('larper.errors.get_by_unique_id_has_multiple')
            raise INCONCEIVABLE(msg % unique_id)
Beispiel #4
0
    def translate(self, instance, src_lang, src_field, dst_lang, dst_field):
        text = getattr(instance, src_field)

        gengo_api = FjordGengo()
        try:
            translated = gengo_api.get_machine_translation(instance.id, text)
            if translated:
                setattr(instance, dst_field, translated)
                instance.save()
                statsd.incr('translation.gengo_machine.success')

            else:
                statsd.incr('translation.gengo_machine.failure')

        except GengoUnknownLanguage:
            # FIXME: This might be an indicator that this response is
            # spam. At some point p, we can write code to account for
            # that.
            statsd.incr('translation.gengo_machine.unknown')

        except GengoUnsupportedLanguage:
            # FIXME: This is a similar boat to GengoUnknownLanguage
            # where for now, we're just going to ignore it because I'm
            # not sure what to do about it and I'd like more data.
            statsd.incr('translation.gengo_machine.unsupported')

        except GengoMachineTranslationFailure:
            # FIXME: For now, if we have a machine translation
            # failure, we're just going to ignore it and move on.
            statsd.incr('translation.gengo_machine.failure')
Beispiel #5
0
def _rebuild_kb_chunk(data):
    """Re-render a chunk of documents.

    Note: Don't use host components when making redirects to wiki pages; those
    redirects won't be auto-pruned when they're 404s.

    """
    log.info('Rebuilding %s documents.' % len(data))

    pin_this_thread()  # Stick to master.

    messages = []
    start = time.time()
    for pk in data:
        message = None
        try:
            document = Document.objects.get(pk=pk)

            # If we know a redirect link to be broken (i.e. if it looks like a
            # link to a document but the document isn't there), log an error:
            url = document.redirect_url()
            if (url and points_to_document_view(url) and
                    not document.redirect_document()):
                log.warn('Invalid redirect document: %d' % pk)

            html = document.parse_and_calculate_links()
            if document.html != html:
                # We are calling update here to so we only update the html
                # column instead of all of them. This bypasses post_save
                # signal handlers like the one that triggers reindexing.
                # See bug 797038 and bug 797352.
                Document.objects.filter(pk=pk).update(html=html)
                statsd.incr('wiki.rebuild_chunk.change')
            else:
                statsd.incr('wiki.rebuild_chunk.nochange')
        except Document.DoesNotExist:
            message = 'Missing document: %d' % pk
        except Revision.DoesNotExist:
            message = 'Missing revision for document: %d' % pk
        except ValidationError as e:
            message = 'ValidationError for %d: %s' % (pk, e.messages[0])
        except SlugCollision:
            message = 'SlugCollision: %d' % pk
        except TitleCollision:
            message = 'TitleCollision: %d' % pk

        if message:
            log.debug(message)
            messages.append(message)
    d = time.time() - start
    statsd.timing('wiki.rebuild_chunk', int(round(d * 1000)))

    if messages:
        subject = ('[%s] Exceptions raised in _rebuild_kb_chunk()' %
                   settings.PLATFORM_NAME)
        mail_admins(subject=subject, message='\n'.join(messages))
    if not transaction.get_connection().in_atomic_block:
        transaction.commit()

    unpin_this_thread()  # Not all tasks need to do use the master.
Beispiel #6
0
def new_thread(request, document_slug):
    """Start a new thread."""
    doc = get_document(document_slug, request)

    if request.method == "GET":
        form = NewThreadForm()
        return render(request, "kbforums/new_thread.html", {"form": form, "document": doc})

    form = NewThreadForm(request.POST)
    post_preview = None
    if form.is_valid():
        if "preview" in request.POST:
            thread = Thread(creator=request.user, title=form.cleaned_data["title"])
            post_preview = Post(thread=thread, creator=request.user, content=form.cleaned_data["content"])
        elif not _is_ratelimited(request):
            thread = doc.thread_set.create(creator=request.user, title=form.cleaned_data["title"])
            thread.save()
            statsd.incr("kbforums.thread")
            post = thread.new_post(creator=request.user, content=form.cleaned_data["content"])
            post.save()

            # Send notifications to forum watchers.
            NewThreadEvent(post).fire(exclude=post.creator)

            # Add notification automatically if needed.
            if Setting.get_for_user(request.user, "kbforums_watch_new_thread"):
                NewPostEvent.notify(request.user, thread)

            return HttpResponseRedirect(reverse("wiki.discuss.posts", args=[document_slug, thread.id]))

    return render(request, "kbforums/new_thread.html", {"form": form, "document": doc, "post_preview": post_preview})
Beispiel #7
0
def helpful_vote(request, document_slug):
    """Vote for Helpful/Not Helpful document"""
    revision = get_object_or_404(
        Revision, id=request.POST['revision_id'])

    if not revision.has_voted(request):
        ua = request.META.get('HTTP_USER_AGENT', '')[:1000]  # 1000 max_length
        vote = HelpfulVote(revision=revision, user_agent=ua)

        if 'helpful' in request.POST:
            vote.helpful = True
            message = _('Glad to hear it — thanks for the feedback!')
        else:
            message = _('Sorry to hear that. Try searching for solutions '
                        'below.')

        if request.user.is_authenticated():
            vote.creator = request.user
        else:
            vote.anonymous_id = request.anonymous.anonymous_id

        vote.save()
        statsd.incr('wiki.vote')
    else:
        message = _('You already voted on this Article.')

    if request.is_ajax():
        return HttpResponse(json.dumps({'message': message}))

    return HttpResponseRedirect(revision.document.get_absolute_url())
Beispiel #8
0
    def get_profile(self):
        """Retrieve the Django UserProfile for this Person.

        This is full of hacks because all the Mozillians servers are throwing
        ObjectDoesNotExist errors (even in production) if we try a straight-up
        `User.objects.get(email=self.username)`. This method now exhaustively
        tries to get a User object from the database. If it doesn't find one,
        or finds one without a UserProfile, we make one on the spot, trying
        our best to fill things in sanely. FML.

        See: https://bugzilla.mozilla.org/show_bug.cgi?id=698699

        TODO: Remove this as soon as possible. It's insane.
        """
        user = (User.objects.filter(Q(email=self.username) | Q(username=self.username)))[:1]

        if user:
            # Yes, sometimes the User exists but the UserProfile doesn't.
            # See: https://bugzilla.mozilla.org/show_bug.cgi?id=699234
            try:
                profile = user[0].get_profile()
            except ObjectDoesNotExist, e:
                statsd.incr("user.errors.profile_doesnotexist")
                log.warning(e)

                profile = UserProfile.objects.create(user=user[0])
Beispiel #9
0
def watch_locale(request):
    """Start watching a locale for revisions ready for review."""
    ReviewableRevisionInLocaleEvent.notify(request.user,
                                           locale=request.LANGUAGE_CODE)
    statsd.incr('wiki.watches.locale')
    # A 200 so jQuery interprets it as success
    return HttpResponse()
Beispiel #10
0
def answer_preview_async(request):
    """Create an HTML fragment preview of the posted wiki syntax."""
    statsd.incr('questions.preview')
    answer = Answer(creator=request.user,
                    content=request.POST.get('content', ''))
    return jingo.render(request, 'questions/includes/answer_preview.html',
                        {'answer_preview': answer})
Beispiel #11
0
    def save(self, request):
        """Save the data to profile."""
        self._save_groups(request)
        user = request.user
        profile = user.get_profile()
        d = self.cleaned_data

        user.first_name = d['first_name']
        user.last_name = d['last_name']

        profile.bio = d['biography']
        profile.ircname = d['irc_nickname']
        profile.website = d['website']

        if d['photo']:
            profile.photo = True
            with open(profile.get_photo_file(), 'w') as f:
                f.write(d['photo'].file.read())

        if d['photo_delete']:
            profile.photo = False
            try:
                os.remove(profile.get_photo_file())
            except OSError:
                statsd.incr('errors.photo.deletion')

        profile.save()
        user.save()
Beispiel #12
0
def answer_vote(request, question_id, answer_id):
    """Vote for Helpful/Not Helpful answers"""
    answer = get_object_or_404(Answer, pk=answer_id, question=question_id)
    if answer.question.is_locked:
        raise PermissionDenied

    if not answer.has_voted(request):
        vote = AnswerVote(answer=answer)

        if 'helpful' in request.REQUEST:
            vote.helpful = True
            AnswerMarkedHelpfulAction(answer.creator).save()
            message = _('Glad to hear it!')
        else:
            AnswerMarkedNotHelpfulAction(answer.creator).save()
            message = _('Sorry to hear that.')

        if request.user.is_authenticated():
            vote.creator = request.user
        else:
            vote.anonymous_id = request.anonymous.anonymous_id

        vote.save()
        ua = request.META.get('HTTP_USER_AGENT')
        if ua:
            vote.add_metadata('ua', ua[:1000])  # 1000 max_length
        statsd.incr('questions.votes.answer')
    else:
        message = _('You already voted on this reply.')

    if request.is_ajax():
        return HttpResponse(json.dumps({'message': message}))

    return HttpResponseRedirect(answer.get_absolute_url())
Beispiel #13
0
def question_vote(request, question_id):
    """I have this problem too."""
    question = get_object_or_404(Question, pk=question_id)
    if question.is_locked:
        raise PermissionDenied

    if not question.has_voted(request):
        vote = QuestionVote(question=question)

        if request.user.is_authenticated():
            vote.creator = request.user
        else:
            vote.anonymous_id = request.anonymous.anonymous_id

        vote.save()
        ua = request.META.get('HTTP_USER_AGENT')
        if ua:
            vote.add_metadata('ua', ua[:1000])  # 1000 max_length
        statsd.incr('questions.votes.question')

        if request.is_ajax():
            tmpl = 'questions/includes/question_vote_thanks.html'
            form = _init_watch_form(request)
            html = jingo.render_to_string(request, tmpl, {'question': question,
                                                          'watch_form': form})
            return HttpResponse(json.dumps({'html': html}))

    return HttpResponseRedirect(question.get_absolute_url())
Beispiel #14
0
    def generate_translation_jobs(self, system=None):
        """Returns a list of tuples, one for each translation job

        If the locale of this response is English, then we just copy over
        the description and we're done.

        If the product of this response isn't set up for
        auto-translation and no translation system was specified in
        the arguments, then we're done.

        If we already have a response with this text that's
        translated, we copy the most recent translation over.

        Otherwise we generate a list of jobs to be done.

        """
        # If the text is in English, we copy it over and we're
        # done. We do this regardless of whether auto-translation is
        # enabled or not for this product.
        if self.locale == 'en-US':
            self.translated_description = self.description
            self.save()
            return []

        if not system:
            try:
                prod = Product.objects.get(db_name=self.product)
                system = prod.translation_system
            except Product.DoesNotExist:
                # If the product doesn't exist, then I don't know
                # what's going on. Regardless, we shouldn't create any
                # translation jobs.
                return []

        if not system:
            # If this product isn't set up for translation, don't
            # translate it.
            return []

        try:
            # See if this text has been translated already--if so, use
            # the most recent translation.
            existing_translation = (Response.objects.filter(
                description=self.description).filter(
                    locale=self.locale).exclude(
                        translated_description__isnull=True).exclude(
                            translated_description=u'').values_list(
                                'translated_description').latest('id'))
            self.translated_description = existing_translation[0]
            self.save()
            statsd.incr('feedback.translation.used_existing')
            return []
        except Response.DoesNotExist:
            pass

        return [
            # key, system, src language, src field, dst language, dst field
            (compose_key(self), system, self.locale, 'description', u'en-US',
             'translated_description')
        ]
Beispiel #15
0
def post_preview_async(request):
    """Ajax preview of posts."""
    statsd.incr('forums.preview')
    post = Post(author=request.user, content=request.POST.get('content', ''))
    post.author_post_count = 1
    return jingo.render(request, 'forums/includes/post_preview.html',
                        {'post_preview': post})
Beispiel #16
0
def topics_for(products, parent=False):
    """Returns a list of topics that apply to passed in products and topics.

    :arg products: a list of Product instances
    :arg parent: (optional) limit to topics with the given parent
    """
    statsd.incr('wiki.facets.topics_for.db')

    docs = Document.objects.filter(locale=settings.WIKI_DEFAULT_LANGUAGE,
                                   is_archived=False,
                                   current_revision__isnull=False,
                                   category__in=settings.IA_DEFAULT_CATEGORIES)

    for product in products:
        docs = docs.filter(products=product)

    for product in products:
        qs = Topic.objects.filter(product=product)

    qs = (qs.filter(
        visible=True,
        document__in=docs).annotate(num_docs=Count('document')).distinct())

    if parent or parent is None:
        qs = qs.filter(parent=parent)

    return qs
Beispiel #17
0
 def creator_num_points(self):
     try:
         return KarmaManager().count(
             'all', user=self.creator, type='points')
     except RedisError as e:
         statsd.incr('redis.errror')
         log.error('Redis connection error: %s' % e)
Beispiel #18
0
def answer_preview_async(request):
    """Create an HTML fragment preview of the posted wiki syntax."""
    statsd.incr('questions.preview')
    answer = Answer(creator=request.user,
                    content=request.POST.get('content', ''))
    return jingo.render(request, 'questions/includes/answer_preview.html',
                        {'answer_preview': answer})
Beispiel #19
0
def activate(request, activation_key, user_id=None):
    """Activate a User account."""
    activation_key = activation_key.lower()

    if user_id:
        user = get_object_or_404(User, id=user_id)
    else:
        user = RegistrationProfile.objects.get_user(activation_key)

    if user and user.is_active:
        messages.add_message(
            request, messages.INFO,
            _(u'Your account is already activated, log in below.'))
        return HttpResponseRedirect(reverse('users.login'))

    account = RegistrationProfile.objects.activate_user(activation_key,
                                                        request)
    my_questions = None
    form = AuthenticationForm()
    if account:
        # Claim anonymous watches belonging to this email
        statsd.incr('user.activate')
        claim_watches.delay(account)

        my_questions = Question.uncached.filter(creator=account)

    return jingo.render(request, 'users/activate.html',
                        {'account': account, 'questions': my_questions,
                         'form': form})
Beispiel #20
0
def _get_registered_user(directory, request):
    """Checks the directory for a registered user.

    Function returns a tuple of registered and details.
    Registered is True if a user is found and False otherwise.
    If registered is True then details contains info about
    the known user.

    The statsd timer ``larper.sasl_bind_time`` allows IT to detect
    timeouts between ldap and https://browserid.org/verify. If this
    counter gets large, check DNS routes between slapd servers and
    browserid.org.

    The statsd counter
    ``browserid.unknown_error_checking_registered_user``
    allows IT to detect a problem with the backend auth system.
    """
    registered = False
    details = None
    try:
        (registered, details) = directory.registered_user()
        if registered:
            request.session['unique_id'] = details
        else:
            request.session['verified_email'] = details
    except Exception, e:
        # Look at syslogs on slapd hosts to investigate unknown issues
        messages.error(request,
                       _("We're Sorry, but Something Went Wrong!"))
        statsd.incr('browserid.unknown_error_checking_registered_user')
        log.error("Unknown error, clearing session assertion [%s]", e)
        store_assertion(request, None)
Beispiel #21
0
def find_related_documents(doc):
    """
    Returns a QuerySet of related_docuemnts or of the
    parent's related_documents in the case of translations
    """
    if doc.locale == settings.WIKI_DEFAULT_LANGUAGE:
        return doc.related_documents.order_by('-related_to__in_common')[0:5]

    # Not English, so may need related docs which are
    # stored on the English version.
    try:
        redis = redis_client('default')
    except RedisError as e:
        # Problem with Redis. Log and return the related docs.
        statsd.incr('redis.errror')
        log.error('Redis error: %s' % e)
        return related_translated_documents(doc)

    doc_key = 'translated_doc_id:%s' % doc.id
    related_ids = redis.lrange(doc_key, 0, -1)
    if related_ids == ['0']:
        return Document.objects.get_empty_query_set()
    if related_ids:
        return Document.objects.filter(id__in=related_ids)

    related = related_translated_documents(doc)
    if not related:
        # Add '0' to prevent recalulation on a known empty set.
        redis.lpush(doc_key, 0)
    else:
        for r in related:
            redis.lpush(doc_key, r.id)
    # Cache expires in 2 hours.
    redis.expire(doc_key, 60 * 60 * 2)
    return related
Beispiel #22
0
def post_preview_async(request):
    """Ajax preview of posts."""
    statsd.incr('forums.preview')
    post = Post(author=request.user, content=request.POST.get('content', ''))
    post.author_post_count = 1
    return jingo.render(request, 'forums/includes/post_preview.html',
                        {'post_preview': post})
Beispiel #23
0
    def sanitize_result(self, result):
        """ Validates grader response `dict` to ensure the LMS can handle it.

        Type coercion for score and correct values, XML validation for msg
        value.

        The LMS is not forgiving if a grader response message contains invalid
        XML. To work around this we run the message through the same XML
        function used in the LMS, and if it raises any exceptions we replace
        this message informing students to notify course staff.

        """
        valid = {}
        try:
            # Santize score / correct
            valid["correct"] = bool(result["correct"])
            valid["score"] = float(result["score"])

            # Ensure response message contains valid XML. If it doesn't,
            # replace it with a message informing students to notify
            # course staff and log the error.
            try:
                etree.fromstring(result["msg"])
                valid["msg"] = result["msg"]
            except etree.XMLSyntaxError as e:
                log.error("Grader response message contains invalid XML: %s (%s)", result["msg"], e)
                valid["msg"] = "<div>Unable to display results. Please report this issue to course staff.</div>"
                statsd.incr("bux_grader_framework.invalid_grader_response")

        except Exception:
            raise InvalidGraderReply("Invalid grader response")

        return valid
Beispiel #24
0
    def handle_submission(self, frame, on_complete):
        """ Handles a submission popped off the dead letter queue.

        Pushes a failure response to XQueue to notify students of the issue.

        """
        submission = frame["submission"]
        submission_id = submission['xqueue_header']['submission_id']
        log.info("Pulled submission #%d off of dead letter queue", submission_id)
        statsd.incr('bux_grader_framework.submissions.dead_lettered')

        # Note time spent in grader
        elapsed_time = int((time.time() - frame["received_time"])*1000.0)
        statsd.timing('bux_grader_framework.total_time_spent', elapsed_time)
        log.info("Submission #%d evaluated in %0.3fms",
                 submission_id, elapsed_time)

        # Check evaluator for extra context to add to fail message.
        hints = ''
        if 'fail_hints' in dir(self.evaluator):
            hints = self.evaluator.fail_hints()

        # Post response to XQueue.
        message = FAIL_RESPONSE.substitute(reason=hints)
        result, success = safe_multi_call(self.xqueue.push_failure,
                                          args=(message, submission),
                                          max_attempts=5,
                                          delay=5)

        # Notifies queue to ack / nack message.
        on_complete(success)
Beispiel #25
0
    def create_inactive_user(self,
                             username,
                             password,
                             email,
                             locale=settings.LANGUAGE_CODE,
                             text_template=None,
                             html_template=None,
                             subject=None,
                             email_data=None,
                             volunteer_interest=False,
                             **kwargs):
        """
        Create a new, inactive ``User`` and ``Profile``, generates a
        ``RegistrationProfile`` and email its activation key to the
        ``User``, returning the new ``User``.
        """
        new_user = User.objects.create_user(username, email, password)
        new_user.is_active = False
        new_user.save()
        Profile.objects.create(user=new_user, locale=locale)

        registration_profile = self.create_profile(new_user)

        self.send_confirmation_email(registration_profile, text_template,
                                     html_template, subject, email_data,
                                     **kwargs)

        if volunteer_interest:
            statsd.incr('user.registered-as-contributor')
            group = Group.objects.get(name=CONTRIBUTOR_GROUP)
            new_user.groups.add(group)

        return new_user
Beispiel #26
0
def question_vote(request, question_id):
    """I have this problem too."""
    question = get_object_or_404(Question, pk=question_id)
    if question.is_locked:
        raise PermissionDenied

    if not question.has_voted(request):
        vote = QuestionVote(question=question)

        if request.user.is_authenticated():
            vote.creator = request.user
        else:
            vote.anonymous_id = request.anonymous.anonymous_id

        vote.save()
        ua = request.META.get('HTTP_USER_AGENT')
        if ua:
            vote.add_metadata('ua', ua[:1000])  # 1000 max_length
        statsd.incr('questions.votes.question')

        if request.is_ajax():
            tmpl = 'questions/includes/question_vote_thanks.html'
            form = _init_watch_form(request)
            html = jingo.render_to_string(request, tmpl, {'question': question,
                                                          'watch_form': form})
            return HttpResponse(json.dumps({'html': html}))

    return HttpResponseRedirect(question.get_absolute_url())
Beispiel #27
0
def new_thread(request, document_slug):
    """Start a new thread."""
    doc = get_document(document_slug, request)

    if request.method == 'GET':
        form = NewThreadForm()
        return jingo.render(request, 'kbforums/new_thread.html',
                            {'form': form, 'document': doc})

    form = NewThreadForm(request.POST)
    post_preview = None
    if form.is_valid():
        if 'preview' in request.POST:
            thread = Thread(creator=request.user,
                            title=form.cleaned_data['title'])
            post_preview = Post(thread=thread, creator=request.user,
                                content=form.cleaned_data['content'])
        else:
            thread = doc.thread_set.create(creator=request.user,
                                             title=form.cleaned_data['title'])
            thread.save()
            statsd.incr('kbforums.thread')
            post = thread.new_post(creator=request.user,
                                   content=form.cleaned_data['content'])
            post.save()

            # Send notifications to forum watchers.
            NewThreadEvent(post).fire(exclude=post.creator)

            return HttpResponseRedirect(
                reverse('wiki.discuss.posts', args=[document_slug, thread.id]))

    return jingo.render(request, 'kbforums/new_thread.html',
                        {'form': form, 'document': doc,
                         'post_preview': post_preview})
Beispiel #28
0
def answer_vote(request, question_id, answer_id):
    """Vote for Helpful/Not Helpful answers"""
    answer = get_object_or_404(Answer, pk=answer_id, question=question_id)
    if answer.question.is_locked:
        raise PermissionDenied

    if not answer.has_voted(request):
        vote = AnswerVote(answer=answer)

        if 'helpful' in request.REQUEST:
            vote.helpful = True
            AnswerMarkedHelpfulAction(answer.creator).save()
            message = _('Glad to hear it!')
        else:
            AnswerMarkedNotHelpfulAction(answer.creator).save()
            message = _('Sorry to hear that.')

        if request.user.is_authenticated():
            vote.creator = request.user
        else:
            vote.anonymous_id = request.anonymous.anonymous_id

        vote.save()
        ua = request.META.get('HTTP_USER_AGENT')
        if ua:
            vote.add_metadata('ua', ua[:1000])  # 1000 max_length
        statsd.incr('questions.votes.answer')
    else:
        message = _('You already voted on this reply.')

    if request.is_ajax():
        return HttpResponse(json.dumps({'message': message}))

    return HttpResponseRedirect(answer.get_absolute_url())
Beispiel #29
0
def preview_async(request):
    """Ajax preview of posts."""
    statsd.incr('forums.preview')
    m = OutboxMessage(sender=request.user,
                      message=request.POST.get('content', ''))
    return render(request, 'messages/includes/message_preview.html',
                  {'message': m})
Beispiel #30
0
def reply(request, document_slug, thread_id):
    """Reply to a thread."""
    doc = get_document(document_slug, request)

    form = ReplyForm(request.POST)
    reply_preview = None
    if form.is_valid():
        thread = get_object_or_404(Thread, pk=thread_id, document=doc)

        if not thread.is_locked:
            reply_ = form.save(commit=False)
            reply_.thread = thread
            reply_.creator = request.user
            if 'preview' in request.POST:
                reply_preview = reply_
            else:
                reply_.save()
                statsd.incr('kbforums.reply')

                # Send notifications to thread/forum watchers.
                NewPostEvent(reply_).fire(exclude=reply_.creator)

                return HttpResponseRedirect(reply_.get_absolute_url())

    return posts(request, document_slug, thread_id, form, reply_preview)
Beispiel #31
0
def reply(request, forum_slug, thread_id):
    """Reply to a thread."""
    forum = get_object_or_404(Forum, slug=forum_slug)
    user = request.user
    if not forum.allows_posting_by(user):
        if forum.allows_viewing_by(user):
            raise PermissionDenied
        else:
            raise Http404

    form = ReplyForm(request.POST)
    reply_preview = None
    if form.is_valid():
        thread = get_object_or_404(Thread, pk=thread_id, forum=forum)

        if not thread.is_locked:
            reply_ = form.save(commit=False)
            reply_.thread = thread
            reply_.author = request.user
            if 'preview' in request.POST:
                reply_preview = reply_
                reply_preview.author_post_count = \
                    reply_.author.post_set.count()
            else:
                reply_.save()
                statsd.incr('forums.reply')

                # Send notifications to thread/forum watchers.
                NewPostEvent(reply_).fire(exclude=reply_.author)

                return HttpResponseRedirect(reply_.get_absolute_url())

    return posts(request, forum_slug, thread_id, form, reply_preview,
                 is_reply=True)
Beispiel #32
0
def topics_for(products, parent=False):
    """Returns a list of topics that apply to passed in products and topics.

    :arg products: a list of Product instances
    :arg parent: (optional) limit to topics with the given parent
    """
    statsd.incr("wiki.facets.topics_for.db")

    docs = Document.objects.filter(
        locale=settings.WIKI_DEFAULT_LANGUAGE,
        is_archived=False,
        current_revision__isnull=False,
        category__in=settings.IA_DEFAULT_CATEGORIES,
    )

    for product in products:
        docs = docs.filter(products=product)

    for product in products:
        qs = Topic.objects.filter(product=product)

    qs = qs.filter(visible=True, document__in=docs).annotate(num_docs=Count("document")).distinct()

    if parent or parent is None:
        qs = qs.filter(parent=parent)

    return qs
Beispiel #33
0
def delete_post(request, document_slug, thread_id, post_id):
    """Delete a post."""
    doc = get_document(document_slug, request)
    thread = get_object_or_404(Thread, pk=thread_id, document=doc)
    post = get_object_or_404(Post, pk=post_id, thread=thread)

    if request.method == 'GET':
        # Render the confirmation page
        return render(request, 'kbforums/confirm_post_delete.html', {
            'document': doc, 'thread': thread, 'post': post})

    # Handle confirm delete form POST
    log.warning("User {0!s} is deleting KB post with id={1!s}".format(request.user, post.id))
    post.delete()

    statsd.incr('kbforums.delete_post')

    try:
        Thread.objects.get(pk=thread_id)
        goto = reverse('wiki.discuss.posts',
                       args=[document_slug, thread_id])
    except Thread.DoesNotExist:
        # The thread was deleted, go to the threads list page
        goto = reverse('wiki.discuss.threads', args=[document_slug])

    return HttpResponseRedirect(goto)
Beispiel #34
0
def watch_document(request, document_slug):
    """Start watching a document for edits."""
    document = get_object_or_404(
        Document, locale=request.LANGUAGE_CODE, slug=document_slug)
    EditDocumentEvent.notify(request.user, document)
    statsd.incr('wiki.watches.document')
    return HttpResponseRedirect(document.get_absolute_url())
Beispiel #35
0
def watch_ready(request):
    """Start watching ready-for-l10n revisions."""
    if request.locale != settings.WIKI_DEFAULT_LANGUAGE:
        raise Http404
    ReadyRevisionEvent.notify(request.user)
    statsd.incr('wiki.watches.ready')
    return HttpResponse()
Beispiel #36
0
def _get_registered_user(directory, request):
    """Checks the directory for a registered user.

    Function returns a tuple of registered and details.
    Registered is True if a user is found and False otherwise.
    If registered is True then details contains info about
    the known user.

    The statsd timer ``larper.sasl_bind_time`` allows IT to detect
    timeouts between ldap and https://browserid.org/verify. If this
    counter gets large, check DNS routes between slapd servers and
    browserid.org.

    The statsd counter
    ``browserid.unknown_error_checking_registered_user``
    allows IT to detect a problem with the backend auth system.
    """
    registered = False
    details = None
    try:
        (registered, details) = directory.registered_user()
        if registered:
            request.session['unique_id'] = details
        else:
            request.session['verified_email'] = details
    except Exception, e:
        # Look at syslogs on slapd hosts to investigate unknown issues
        messages.error(request, _("We're Sorry, but Something Went Wrong!"))
        statsd.incr('browserid.unknown_error_checking_registered_user')
        log.error("Unknown error, clearing session assertion [%s]", e)
        store_assertion(request, None)
Beispiel #37
0
def activate(request, template, activation_key, user_id=None):
    """Activate a User account."""
    activation_key = activation_key.lower()

    if user_id:
        user = get_object_or_404(User, id=user_id)
    else:
        user = RegistrationProfile.objects.get_user(activation_key)

    if user and user.is_active:
        messages.add_message(request, messages.INFO, _(u"Your account is already activated, log in below."))
        return HttpResponseRedirect(reverse("users.login"))

    account = RegistrationProfile.objects.activate_user(activation_key, request)
    my_questions = None
    form = AuthenticationForm()
    if account:
        # Claim anonymous watches belonging to this email
        statsd.incr("user.activate")
        claim_watches.delay(account)

        my_questions = Question.uncached.filter(creator=account)

        # Update created time to current time
        for q in my_questions:
            q.created = datetime.now()
            q.save(update=True)

    return render(request, template, {"account": account, "questions": my_questions, "form": form})
Beispiel #38
0
def handle_register(request, email_template=None, email_subject=None,
                    email_data=None):
    """Handle to help registration."""
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            form = try_send_email_with_form(
                RegistrationProfile.objects.create_inactive_user,
                form, 'email',
                form.cleaned_data['username'],
                form.cleaned_data['password'],
                form.cleaned_data['email'],
                locale=request.locale,
                email_template=email_template,
                email_subject=email_subject,
                email_data=email_data)
            if not form.is_valid():
                # Delete user if form is not valid, i.e. email was not sent.
                # This is in a POST request and so always pinned to master,
                # so there is no race condition.
                User.objects.filter(email=form.instance.email).delete()
            else:
                statsd.incr('user.register')
        return form
    return RegisterForm()
Beispiel #39
0
    def create_inactive_user(self, username, password, email,
                             locale=settings.LANGUAGE_CODE,
                             email_template=None, email_subject=None,
                             email_data=None, volunteer_interest=False):
        """
        Create a new, inactive ``User`` and ``Profile``, generates a
        ``RegistrationProfile`` and email its activation key to the
        ``User``, returning the new ``User``.
        """
        new_user = User.objects.create_user(username, email, password)
        new_user.is_active = False
        new_user.save()
        Profile.objects.create(user=new_user, locale=locale)

        registration_profile = self.create_profile(new_user)

        self.send_confirmation_email(registration_profile, email_template,
                                     email_subject, email_data)

        if volunteer_interest:
            statsd.incr('user.registered-as-contributor')
            group = Group.objects.get(name=CONTRIBUTOR_GROUP)
            new_user.groups.add(group)

        return new_user
Beispiel #40
0
def watch_document(request, document_slug):
    """Start watching a document for edits."""
    document = get_object_or_404(
        Document, locale=request.LANGUAGE_CODE, slug=document_slug)
    EditDocumentEvent.notify(request.user, document)
    statsd.incr('wiki.watches.document')
    return HttpResponseRedirect(document.get_absolute_url())
Beispiel #41
0
def preview_async(request):
    """Ajax preview of posts."""
    statsd.incr('forums.preview')
    m = OutboxMessage(sender=request.user,
                      message=request.POST.get('content', ''))
    return render(request, 'messages/includes/message_preview.html', {
        'message': m})
Beispiel #42
0
 def creator_num_points(self):
     try:
         return KarmaManager().count(
             'all', user=self.creator, type='points')
     except RedisError as e:
         statsd.incr('redis.errror')
         log.error('Redis connection error: %s' % e)
Beispiel #43
0
def watch_ready(request):
    """Start watching ready-for-l10n revisions."""
    if request.locale != settings.WIKI_DEFAULT_LANGUAGE:
        raise Http404
    ReadyRevisionEvent.notify(request.user)
    statsd.incr('wiki.watches.ready')
    return HttpResponse()
Beispiel #44
0
def submit_ticket(email, category, subject, body):
    """Submit a marketplace ticket to Zendesk.

    :arg email: user's email address
    :arg category: issue's category
    :arg subject: issue's subject
    :arg body: issue's description
    """
    # Create the Zendesk connection client.
    zendesk = get_zendesk()

    # Create the ticket
    new_ticket = {
        'ticket': {
            'requester_email': email,
            'subject': settings.ZENDESK_SUBJECT_PREFIX + subject,
            'description': body,
            'set_tags': category,
        }
    }
    try:
        ticket_url = zendesk.create_ticket(data=new_ticket)
        statsd.incr('questions.zendesk.success')
    except ZendeskError as e:
        log.error('Zendesk error: %s' % e.msg)
        statsd.incr('questions.zendesk.error')
        raise

    return ticket_url
Beispiel #45
0
def watch_approved(request):
    """Start watching approved revisions in a locale."""
    if request.locale not in settings.SUMO_LANGUAGES:
        raise Http404
    ApproveRevisionInLocaleEvent.notify(request.user, locale=request.locale)
    statsd.incr('wiki.watches.approved')
    return HttpResponse()
Beispiel #46
0
def delete_post(request, forum_slug, thread_id, post_id):
    """Delete a post."""
    forum = get_object_or_404(Forum, slug=forum_slug)
    thread = get_object_or_404(Thread, pk=thread_id, forum=forum)
    post = get_object_or_404(Post, pk=post_id, thread=thread)

    if request.method == 'GET':
        # Render the confirmation page
        return render(request, 'forums/confirm_post_delete.html', {
            'forum': forum, 'thread': thread, 'post': post})

    # Handle confirm delete form POST
    log.warning("User %s is deleting post with id=%s" %
                (request.user, post.id))
    post.delete()

    statsd.incr('forums.delete_post')

    try:
        Thread.objects.get(pk=thread_id)
        goto = reverse('forums.posts', args=[forum_slug, thread_id])
    except Thread.DoesNotExist:
        # The thread was deleted, go to the threads list page
        goto = reverse('forums.threads', args=[forum_slug])

    return HttpResponseRedirect(goto)
Beispiel #47
0
def reply(request, document_slug, thread_id):
    """Reply to a thread."""
    doc = get_document(document_slug, request)

    form = ReplyForm(request.POST)
    post_preview = None
    if form.is_valid():
        thread = get_object_or_404(Thread, pk=thread_id, document=doc)

        if not thread.is_locked:
            reply_ = form.save(commit=False)
            reply_.thread = thread
            reply_.creator = request.user
            if 'preview' in request.POST:
                post_preview = reply_
            elif not _is_ratelimited(request):
                reply_.save()
                statsd.incr('kbforums.reply')

                # Subscribe the user to the thread.
                if Setting.get_for_user(request.user,
                                        'kbforums_watch_after_reply'):
                    NewPostEvent.notify(request.user, thread)

                # Send notifications to thread/forum watchers.
                NewPostEvent(reply_).fire(exclude=reply_.creator)

                return HttpResponseRedirect(reply_.get_absolute_url())

    return posts(request, document_slug, thread_id, form, post_preview)
Beispiel #48
0
def watch_approved(request):
    """Start watching approved revisions in a locale."""
    if request.locale not in settings.SUMO_LANGUAGES:
        raise Http404
    ApproveRevisionInLocaleEvent.notify(request.user, locale=request.locale)
    statsd.incr('wiki.watches.approved')
    return HttpResponse()
Beispiel #49
0
def collect_tweets():
    # Don't (ab)use the twitter API from dev and stage.
    if settings.STAGE:
        return

    """Collect new tweets about Firefox."""
    with statsd.timer('customercare.tweets.time_elapsed'):
        t = Twython(settings.TWITTER_CONSUMER_KEY,
                    settings.TWITTER_CONSUMER_SECRET,
                    settings.TWITTER_ACCESS_TOKEN,
                    settings.TWITTER_ACCESS_TOKEN_SECRET)

        search_options = {
            'q': ('firefox OR #fxinput OR @firefoxbrasil OR #firefoxos '
                  'OR @firefox_es'),
            'count': settings.CC_TWEETS_PERPAGE,  # Items per page.
            'result_type': 'recent',  # Retrieve tweets by date.
        }

        # If we already have some tweets, collect nothing older than what we
        # have.
        try:
            latest_tweet = Tweet.latest()
        except Tweet.DoesNotExist:
            log.debug('No existing tweets. Retrieving %d tweets from search.' %
                      settings.CC_TWEETS_PERPAGE)
        else:
            search_options['since_id'] = latest_tweet.tweet_id
            log.info('Retrieving tweets with id >= %s' % latest_tweet.tweet_id)

        # Retrieve Tweets
        results = t.search(**search_options)

        if len(results['statuses']) == 0:
            # Twitter returned 0 results.
            return

        # Drop tweets into DB
        for item in results['statuses']:
            # Apply filters to tweet before saving
            # Allow links in #fxinput tweets
            statsd.incr('customercare.tweet.collected')
            item = _filter_tweet(item,
                                 allow_links='#fxinput' in item['text'])
            if not item:
                continue

            created_date = datetime.utcfromtimestamp(calendar.timegm(
                rfc822.parsedate(item['created_at'])))

            item_lang = item['metadata'].get('iso_language_code', 'en')

            tweet = Tweet(tweet_id=item['id'], raw_json=json.dumps(item),
                          locale=item_lang, created=created_date)
            try:
                tweet.save()
                statsd.incr('customercare.tweet.saved')
            except IntegrityError:
                pass
Beispiel #50
0
def preview_revision(request):
    """Create an HTML fragment preview of the posted wiki syntax."""
    wiki_content = request.POST.get('content', '')
    statsd.incr('wiki.preview')
    # TODO: Get doc ID from JSON.
    data = {'content': wiki_to_html(wiki_content, request.locale)}
    data.update(SHOWFOR_DATA)
    return jingo.render(request, 'wiki/preview.html', data)
Beispiel #51
0
def _save_rev_and_notify(rev_form, creator, document):
    """Save the given RevisionForm and send notifications."""
    new_rev = rev_form.save(creator, document)
    statsd.incr('wiki.revision')

    # Enqueue notifications
    ReviewableRevisionInLocaleEvent(new_rev).fire(exclude=new_rev.creator)
    EditDocumentEvent(new_rev).fire(exclude=new_rev.creator)
Beispiel #52
0
def preview_revision(request):
    """Create an HTML fragment preview of the posted wiki syntax."""
    wiki_content = request.POST.get('content', '')
    statsd.incr('wiki.preview')
    # TODO: Get doc ID from JSON.
    data = {'content': wiki_to_html(wiki_content, request.LANGUAGE_CODE)}
    data.update(showfor_data())
    return render(request, 'wiki/preview.html', data)