Exemple #1
0
def add_comment(request):
    """Add a comment."""
    form = forms.AddCommentsForm(request.POST)
    if not form.is_valid():
        return JsonResponse(
            {
                "status":
                False,
                "message":
                "{error}".format(error=form.errors.as_json(escape_html=True)),
            },
            status=400,
        )

    user = request.user
    comment = form.cleaned_data["comment"]
    translationId = form.cleaned_data["translation"]
    entity = get_object_or_404(Entity, pk=form.cleaned_data["entity"])
    locale = get_object_or_404(Locale, code=form.cleaned_data["locale"])
    if translationId:
        translation = get_object_or_404(Translation, pk=translationId)

    if translationId:
        c = Comment(author=user, translation=translation, content=comment)
        log_action("comment:added", user, translation=translation)
    else:
        c = Comment(author=user, entity=entity, locale=locale, content=comment)
        log_action("comment:added", user, entity=entity, locale=locale)

    c.save()

    return JsonResponse({"status": True})
Exemple #2
0
def test_log_action_unknown_action_type(user_a, translation_a):
    # We send an unsupported action type.
    with pytest.raises(ValidationError):
        utils.log_action(
            "test:unknown",
            user_a,
            translation=translation_a,
        )
Exemple #3
0
def test_log_action_deleted_wrong_keys(user_a, translation_a):
    # We send the wrong parameters for the "deleted" action.
    with pytest.raises(ValidationError):
        utils.log_action(
            "translation:deleted",
            user_a,
            translation=translation_a,
        )
Exemple #4
0
def test_log_action_deleted_wrong_keys(user_a, translation_a):
    # We send the wrong parameters for the "deleted" action.
    with pytest.raises(ValidationError):
        utils.log_action(
            ActionLog.ActionType.TRANSLATION_DELETED,
            user_a,
            translation=translation_a,
        )
Exemple #5
0
def unreject_translation(request):
    """Unreject given translation."""
    try:
        t = request.POST["translation"]
        paths = request.POST.getlist("paths[]")
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    translation = get_object_or_404(Translation, pk=t)
    project = translation.entity.resource.project
    locale = translation.locale

    # Read-only translations cannot be un-rejected
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    # Only privileged users or authors can un-reject translations
    if not (request.user.can_translate(locale, project)
            or request.user == translation.user or translation.approved):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: You can't unreject this translation.",
            },
            status=403,
        )

    translation.unreject(request.user)

    log_action(
        ActionLog.ActionType.TRANSLATION_UNREJECTED,
        request.user,
        translation=translation,
    )

    active_translation = translation.entity.reset_active_translation(
        locale=locale,
        plural_form=translation.plural_form,
    )

    return JsonResponse({
        "translation":
        active_translation.serialize(),
        "stats":
        TranslatedResource.objects.stats(project, paths, locale),
    })
Exemple #6
0
def test_log_action_non_deleted_wrong_keys(user_a, entity_a, locale_a):
    # We send the wrong parameters for a non-"deleted" action.
    with pytest.raises(ValidationError):
        utils.log_action(
            "translation:approved",
            user_a,
            entity=entity_a,
            locale=locale_a,
        )
Exemple #7
0
def test_log_action_non_deleted_wrong_keys(user_a, entity_a, locale_a):
    # We send the wrong parameters for a non-"deleted" action.
    with pytest.raises(ValidationError):
        utils.log_action(
            ActionLog.ActionType.TRANSLATION_APPROVED,
            user_a,
            entity=entity_a,
            locale=locale_a,
        )
Exemple #8
0
def test_log_action_valid_with_translation(user_a, translation_a):
    utils.log_action(
        ActionLog.ActionType.TRANSLATION_CREATED,
        user_a,
        translation=translation_a,
    )

    log = ActionLog.objects.filter(performed_by=user_a, translation=translation_a)
    assert len(log) == 1
    assert log[0].action_type == ActionLog.ActionType.TRANSLATION_CREATED
Exemple #9
0
def test_log_action_valid_with_translation(user_a, translation_a):
    utils.log_action(
        "translation:created",
        user_a,
        translation=translation_a,
    )

    log = ActionLog.objects.filter(performed_by=user_a,
                                   translation=translation_a)
    assert len(log) == 1
    assert log[0].action_type == "translation:created"
Exemple #10
0
def delete_translation(request):
    """Delete given translation."""
    try:
        translation_id = request.POST["translation"]
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    translation = get_object_or_404(Translation, pk=translation_id)
    entity = translation.entity
    project = entity.resource.project
    locale = translation.locale

    # Read-only translations cannot be deleted
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    # Only privileged users or authors can delete translations
    if not translation.rejected or not (request.user.can_translate(
            locale, project) or request.user == translation.user
                                        or translation.approved):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: You can't delete this translation.",
            },
            status=403,
        )

    translation.delete()

    log_action(
        ActionLog.ActionType.TRANSLATION_DELETED,
        request.user,
        entity=entity,
        locale=locale,
    )

    return JsonResponse({"status": True})
Exemple #11
0
def test_log_action_valid_with_entity_locale(user_a, entity_a, locale_a):
    utils.log_action(
        ActionLog.ActionType.TRANSLATION_DELETED,
        user_a,
        entity=entity_a,
        locale=locale_a,
    )

    log = ActionLog.objects.filter(
        performed_by=user_a,
        entity=entity_a,
        locale=locale_a,
    )
    assert len(log) == 1
    assert log[0].action_type == ActionLog.ActionType.TRANSLATION_DELETED
Exemple #12
0
def test_log_action_valid_with_entity_locale(user_a, entity_a, locale_a):
    utils.log_action(
        "translation:deleted",
        user_a,
        entity=entity_a,
        locale=locale_a,
    )

    log = ActionLog.objects.filter(
        performed_by=user_a,
        entity=entity_a,
        locale=locale_a,
    )
    assert len(log) == 1
    assert log[0].action_type == "translation:deleted"
Exemple #13
0
def add_comment(request):
    """Add a comment."""
    form = forms.AddCommentForm(request.POST)
    if not form.is_valid():
        return JsonResponse(
            {
                "status":
                False,
                "message":
                "{error}".format(error=form.errors.as_json(escape_html=True)),
            },
            status=400,
        )

    user = request.user
    comment = form.cleaned_data["comment"]
    translationId = form.cleaned_data["translation"]
    entity = get_object_or_404(Entity, pk=form.cleaned_data["entity"])
    locale = get_object_or_404(Locale, code=form.cleaned_data["locale"])

    if translationId:
        translation = get_object_or_404(Translation, pk=translationId)
    else:
        translation = None

    # Translation comment
    if translation:
        c = Comment(author=user, translation=translation, content=comment)
        log_action(ActionLog.ActionType.COMMENT_ADDED,
                   user,
                   translation=translation)

    # Team comment
    else:
        c = Comment(author=user, entity=entity, locale=locale, content=comment)
        log_action(ActionLog.ActionType.COMMENT_ADDED,
                   user,
                   entity=entity,
                   locale=locale)

    c.save()

    _send_add_comment_notifications(user, comment, entity, locale, translation)

    return JsonResponse({"status": True})
Exemple #14
0
def add_comment(request):
    # TODO: Remove as part of bug 1361318
    return JsonResponse(
        {
            "status": False,
            "message": "Not Implemented"
        },
        status=501,
    )
    """Add a comment."""
    try:
        comment = request.POST["comment"]
        comment = bleach.clean(
            comment,
            strip=True,
            tags=settings.ALLOWED_TAGS,
            attributes=settings.ALLOWED_ATTRIBUTES,
        )
        translationId = request.POST["translationId"]
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    user = request.user
    translation = get_object_or_404(Translation, pk=translationId)

    c = Comment(
        author=user,
        translation=translation,
        content=comment,
    )

    c.save()

    log_action("comment:added", user, translation=translation)

    return JsonResponse({"status": True})
Exemple #15
0
def test_log_action_missing_arg(user_a, entity_a, locale_a):
    # No translation nor (entity and locale) pair.
    with pytest.raises(ValidationError):
        utils.log_action("translation:created", user_a)

    # Missing locale.
    with pytest.raises(ValidationError):
        utils.log_action("translation:deleted", user_a, entity=entity_a)

    # Missing entity.
    with pytest.raises(ValidationError):
        utils.log_action("translation:deleted", user_a, locale=locale_a)
Exemple #16
0
def test_log_action_missing_arg(user_a, entity_a, locale_a):
    # No translation nor (entity and locale) pair.
    with pytest.raises(ValidationError):
        utils.log_action(ActionLog.ActionType.TRANSLATION_CREATED, user_a)

    # Missing locale.
    with pytest.raises(ValidationError):
        utils.log_action(ActionLog.ActionType.TRANSLATION_DELETED,
                         user_a,
                         entity=entity_a)

    # Missing entity.
    with pytest.raises(ValidationError):
        utils.log_action(ActionLog.ActionType.TRANSLATION_DELETED,
                         user_a,
                         locale=locale_a)
Exemple #17
0
def update_translation(request):
    """Update entity translation for the specified locale and user.

    Note that this view is also used to approve a translation by the old
    Translate app. Once we migrate to Translate.Next, we'll want to rework
    this view to remove the bits about approving a translation, because that
    has been delegated to the `approve_translation` view.

    """
    try:
        entity = request.POST["entity"]
        string = request.POST["translation"]
        locale = request.POST["locale"]
        plural_form = request.POST["plural_form"]
        original = request.POST["original"]
        ignore_warnings = request.POST.get("ignore_warnings",
                                           "false") == "true"
        approve = request.POST.get("approve", "false") == "true"
        force_suggestions = request.POST.get("force_suggestions",
                                             "false") == "true"
        paths = request.POST.getlist("paths[]")
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    try:
        locale = Locale.objects.get(code=locale)
    except Locale.DoesNotExist as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    if plural_form == "-1":
        plural_form = None

    user = request.user
    project = e.resource.project

    # Read-only translations cannot saved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    now = timezone.now()
    can_translate = request.user.can_translate(
        project=project, locale=locale) and (not force_suggestions or approve)
    translations = Translation.objects.filter(entity=e,
                                              locale=locale,
                                              plural_form=plural_form)

    same_translations = translations.filter(string=string).order_by(
        "-active", "rejected", "-date")

    # If same translation exists in the DB, don't save it again.
    if utils.is_same(same_translations, can_translate):
        return JsonResponse({"same": True})

    # Check for errors.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != "tutorial"

    failed_checks = None
    if use_checks:
        failed_checks = run_checks(
            e,
            locale.code,
            original,
            string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({"failedChecks": failed_checks})

    # Translations exist
    if len(translations) > 0:
        # Same translation exists
        if len(same_translations) > 0:
            t = same_translations[0]

            # If added by privileged user, approve and unfuzzy it
            if can_translate and (t.fuzzy or not t.approved):
                if not t.active:
                    translations.filter(active=True).update(active=False)
                    t.active = True

                t.approved = True
                t.fuzzy = False
                t.rejected = False
                t.rejected_user = None
                t.rejected_date = None

                if t.approved_user is None:
                    t.approved_user = user
                    t.approved_date = now

                t.save(failed_checks=failed_checks)

                log_action("translation:approved", user, translation=t)

                return JsonResponse({
                    "type":
                    "updated",
                    "translation":
                    t.serialize(),
                    "stats":
                    TranslatedResource.objects.stats(project, paths, locale),
                })

        # Different translation added
        else:
            t = Translation(
                entity=e,
                locale=locale,
                plural_form=plural_form,
                string=string,
                user=user,
                date=now,
                approved=can_translate,
            )

            if can_translate:
                t.approved_user = user
                t.approved_date = now

            t.save(failed_checks=failed_checks)

            log_action("translation:created", user, translation=t)

            active_translation = e.reset_active_translation(
                locale=locale,
                plural_form=plural_form,
            )

            return JsonResponse({
                "type":
                "added",
                "translation":
                active_translation.serialize(),
                "stats":
                TranslatedResource.objects.stats(project, paths, locale),
            })

    # No translations saved yet
    else:
        t = Translation(
            entity=e,
            locale=locale,
            plural_form=plural_form,
            string=string,
            user=user,
            date=now,
            active=True,
            approved=can_translate,
        )

        if can_translate:
            t.approved_user = user
            t.approved_date = now

        t.save(failed_checks=failed_checks)

        log_action("translation:created", user, translation=t)

        return JsonResponse({
            "type":
            "saved",
            "translation":
            t.serialize(),
            "stats":
            TranslatedResource.objects.stats(project, paths, locale),
        })
Exemple #18
0
def create_translation(request):
    """
    Create a new translation.
    """
    form = forms.CreateTranslationForm(request.POST)

    if not form.is_valid():
        problems = []
        for field, errors in form.errors.items():
            problems.append('Error validating field `{0}`: "{1}"'.format(
                field, " ".join(errors)))
        return JsonResponse({
            "status": False,
            "message": "\n".join(problems)
        },
                            status=400)

    entity = form.cleaned_data["entity"]
    string = form.cleaned_data["translation"]
    locale = form.cleaned_data["locale"]
    plural_form = form.cleaned_data["plural_form"]
    original = form.cleaned_data["original"]
    ignore_warnings = form.cleaned_data["ignore_warnings"]
    approve = form.cleaned_data["approve"]
    force_suggestions = form.cleaned_data["force_suggestions"]
    resources = form.cleaned_data["paths"]

    project = entity.resource.project

    # Read-only translations cannot saved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    translations = Translation.objects.filter(entity=entity,
                                              locale=locale,
                                              plural_form=plural_form)

    same_translations = translations.filter(string=string)

    # If same translation exists in the DB, don't save it again.
    if same_translations:
        return JsonResponse({"status": False, "same": True})

    # Look for failed checks.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != "tutorial"
    user = request.user

    failed_checks = None
    if use_checks:
        failed_checks = run_checks(
            entity,
            locale.code,
            original,
            string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({
                "status": False,
                "failedChecks": failed_checks
            })

    now = timezone.now()
    can_translate = user.can_translate(
        project=project, locale=locale) and (not force_suggestions or approve)

    translation = Translation(
        entity=entity,
        locale=locale,
        plural_form=plural_form,
        string=string,
        user=user,
        date=now,
        approved=can_translate,
    )

    if can_translate:
        translation.approved_user = user
        translation.approved_date = now

    translation.save(failed_checks=failed_checks)

    log_action("translation:created", user, translation=translation)

    if translations:
        translation = entity.reset_active_translation(
            locale=locale,
            plural_form=plural_form,
        )

    return JsonResponse({
        "status":
        True,
        "translation":
        translation.serialize(),
        "stats":
        TranslatedResource.objects.stats(project, resources, locale),
    })
Exemple #19
0
def reject_translation(request):
    """Reject given translation."""
    try:
        t = request.POST["translation"]
        paths = request.POST.getlist("paths[]")
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    translation = get_object_or_404(Translation, pk=t)
    project = translation.entity.resource.project
    locale = translation.locale

    # Read-only translations cannot be rejected
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    # Non-privileged users can only reject own unapproved translations
    if not request.user.can_translate(locale, project):
        if translation.user == request.user:
            if translation.approved is True:
                return JsonResponse(
                    {
                        "status":
                        False,
                        "message":
                        "Forbidden: You can't reject approved translations.",
                    },
                    status=403,
                )
        else:
            return JsonResponse(
                {
                    "status":
                    False,
                    "message":
                    "Forbidden: You can't reject translations from other users.",
                },
                status=403,
            )

    translation.reject(request.user)

    log_action("translation:rejected", request.user, translation=translation)

    active_translation = translation.entity.reset_active_translation(
        locale=locale,
        plural_form=translation.plural_form,
    )

    return JsonResponse({
        "translation":
        active_translation.serialize(),
        "stats":
        TranslatedResource.objects.stats(project, paths, locale),
    })
Exemple #20
0
def approve_translation(request):
    """Approve given translation."""
    try:
        t = request.POST["translation"]
        ignore_warnings = request.POST.get("ignore_warnings",
                                           "false") == "true"
        paths = request.POST.getlist("paths[]")
    except MultiValueDictKeyError as e:
        return JsonResponse(
            {
                "status": False,
                "message": "Bad Request: {error}".format(error=e)
            },
            status=400,
        )

    translation = get_object_or_404(Translation, pk=t)
    entity = translation.entity
    project = entity.resource.project
    locale = translation.locale
    user = request.user

    # Read-only translations cannot be approved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    if translation.approved:
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This translation is already approved.",
            },
            status=403,
        )

    # Only privileged users can approve translations
    if not user.can_translate(locale, project):
        return JsonResponse(
            {
                "status":
                False,
                "message":
                "Forbidden: You don't have permission to approve this translation.",
            },
            status=403,
        )

    # Check for errors.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != "tutorial"

    if use_checks:
        failed_checks = run_checks(
            entity,
            locale.code,
            entity.string,
            translation.string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({
                "string": translation.string,
                "failedChecks": failed_checks
            })

    translation.approve(user)

    log_action("translation:approved", user, translation=translation)

    active_translation = translation.entity.reset_active_translation(
        locale=locale,
        plural_form=translation.plural_form,
    )

    return JsonResponse({
        "translation":
        active_translation.serialize(),
        "stats":
        TranslatedResource.objects.stats(project, paths, locale),
    })
Exemple #21
0
def create_translation(request):
    """
    Create a new translation.
    """
    form = forms.CreateTranslationForm(request.POST)

    if not form.is_valid():
        problems = []
        for field, errors in form.errors.items():
            problems.append(
                'Error validating field `{}`: "{}"'.format(field, " ".join(errors))
            )
        return JsonResponse(
            {"status": False, "message": "\n".join(problems)}, status=400
        )

    entity = form.cleaned_data["entity"]
    string = form.cleaned_data["translation"]
    locale = form.cleaned_data["locale"]
    plural_form = form.cleaned_data["plural_form"]
    original = form.cleaned_data["original"]
    ignore_warnings = form.cleaned_data["ignore_warnings"]
    approve = form.cleaned_data["approve"]
    force_suggestions = form.cleaned_data["force_suggestions"]
    paths = form.cleaned_data["paths"]
    machinery_sources = form.cleaned_data["machinery_sources"]

    project = entity.resource.project

    # Read-only translations cannot saved
    if utils.readonly_exists(project, locale):
        return JsonResponse(
            {
                "status": False,
                "message": "Forbidden: This string is in read-only mode.",
            },
            status=403,
        )

    translations = Translation.objects.filter(
        entity=entity,
        locale=locale,
        plural_form=plural_form,
    )

    same_translations = translations.filter(string=string)

    # If same translation exists in the DB, don't save it again.
    if same_translations:
        return JsonResponse({"status": False, "same": True})

    # Look for failed checks.
    # Checks are disabled for the tutorial.
    use_checks = project.slug != "tutorial"
    user = request.user
    first_contribution = user.is_new_contributor(locale)

    failed_checks = None
    if use_checks:
        failed_checks = run_checks(
            entity,
            locale.code,
            original,
            string,
            user.profile.quality_checks,
        )

        if are_blocking_checks(failed_checks, ignore_warnings):
            return JsonResponse({"status": False, "failedChecks": failed_checks})

    now = timezone.now()
    can_translate = user.can_translate(project=project, locale=locale) and (
        not force_suggestions or approve
    )

    translation = Translation(
        entity=entity,
        locale=locale,
        plural_form=plural_form,
        string=string,
        user=user,
        date=now,
        approved=can_translate,
        machinery_sources=machinery_sources,
    )

    if can_translate:
        translation.approved_user = user
        translation.approved_date = now

    translation.save(failed_checks=failed_checks)

    log_action(ActionLog.ActionType.TRANSLATION_CREATED, user, translation=translation)

    if translations:
        translation = entity.reset_active_translation(
            locale=locale,
            plural_form=plural_form,
        )

    # When user makes their first contribution to the team, notify team managers
    if first_contribution:
        desc = """
        <a href="{user_href}">{user}</a> has made their first contribution to
        <a href="{locale_href}">{locale} ({locale_code})</a>.
        Please welcome them to the team, and make sure to
        <a href="{review_href}">review their suggestions</a>.
        """.format(
            user=user.name_or_email,
            user_href=reverse(
                "pontoon.contributors.contributor.username",
                kwargs={
                    "username": user.username,
                },
            ),
            locale=locale.name,
            locale_code=locale.code,
            locale_href=reverse(
                "pontoon.teams.team",
                kwargs={
                    "locale": locale.code,
                },
            ),
            review_href=reverse(
                "pontoon.translate",
                kwargs={
                    "locale": locale.code,
                    "project": project.slug,
                    "resource": entity.resource.path,
                },
            )
            + f"?string={entity.pk}",
        )

        for manager in locale.managers_group.user_set.filter(
            profile__new_contributor_notifications=True
        ):
            notify.send(
                sender=manager,
                recipient=manager,
                verb="has reviewed suggestions",  # Triggers render of description only
                description=desc,
            )

    return JsonResponse(
        {
            "status": True,
            "translation": translation.serialize(),
            "stats": TranslatedResource.objects.stats(project, paths, locale),
        }
    )