Esempio n. 1
0
def save_translation(entity, locale, string, plural_form=None, fuzzy=False):
    """Admin interface: save new or update existing translation in DB."""

    approved = not fuzzy
    translations = Translation.objects.filter(
        entity=entity, locale=locale, plural_form=plural_form)
    translations_equal = translations.filter(string=string)
    translations_equal_count = translations_equal.count()

    # Save new translation if it doesn's exist yet
    if translations_equal_count == 0:
        unset_approved(translations)
        t = Translation(
            entity=entity, locale=locale, plural_form=plural_form,
            string=string, date=datetime.datetime.now(),
            approved=approved, fuzzy=fuzzy)
        t.save(stats=False)

    # Update existing translations if fuzzy status changes
    elif translations_equal_count > 0:
        t = translations_equal[0]
        if translations_equal_count > 1:
            try:
                t = translations_equal.get(approved=True)
            except Translation.DoesNotExist:
                t = translations_equal.latest("date")

        if t.fuzzy != fuzzy:
            unset_approved(translations)
            t.date = datetime.datetime.now()
            t.approved = approved
            t.fuzzy = fuzzy
            t.save(stats=False)
Esempio n. 2
0
def translate(request, locale, slug, part):
    """Translate view."""
    locale = get_object_or_404(Locale, code__iexact=locale)
    project = get_object_or_404(Project.objects.available(), slug=slug)

    if locale not in project.locales.all():
        raise Http404

    projects = (
        Project.objects.available()
        .prefetch_related('subpage_set')
        .order_by('name')
    )

    paths = [part] if part != 'all-resources' else None
    translations = Translation.for_locale_project_paths(locale, project, paths)

    return render(request, 'translate.html', {
        'download_form': forms.DownloadFileForm(),
        'upload_form': forms.UploadFileForm(),
        'locale': locale,
        'locales': Locale.objects.available(),
        'part': part,
        'project': project,
        'projects': projects,
        'authors': translations.authors().serialize(),
        'counts_per_minute': translations.counts_per_minute(),
    })
Esempio n. 3
0
def translate(request, locale, slug, part):
    """Translate view."""
    locale = get_object_or_404(Locale, code__iexact=locale)
    project = get_object_or_404(Project.objects.available(), slug=slug)

    if locale not in project.locales.all():
        raise Http404

    projects = (
        Project.objects.available()
        .prefetch_related('subpage_set')
        .order_by('name')
    )

    paths = [part] if part != 'all-resources' else None
    authors = Translation.authors(locale, project, paths).serialize()

    return render(request, 'translate.html', {
        'download_form': forms.DownloadFileForm(),
        'upload_form': forms.UploadFileForm(),
        'locale': locale,
        'locales': Locale.objects.available(),
        'part': part,
        'project': project,
        'projects': projects,
        'authors': authors,
    })
Esempio n. 4
0
def delete_translation(request):
    """Delete given translation."""
    try:
        t = request.POST['translation']
        paths = request.POST.getlist('paths[]')
    except MultiValueDictKeyError as e:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    translation = get_object_or_404(Translation, pk=t)

    # Non-privileged users can only delete own unapproved translations
    if not request.user.has_perm('base.can_translate_locale', translation.locale):
        if translation.user == request.user:
            if translation.approved is True:
                return HttpResponseForbidden(
                    "Forbidden: Can't delete approved translations"
                )
        else:
            return HttpResponseForbidden(
                "Forbidden: Can't delete translations from other users"
            )

    translation.delete()

    project = translation.entity.resource.project
    locale = translation.locale
    return JsonResponse({
        'stats': TranslatedResource.objects.stats(project, paths, locale),
        'authors': Translation.authors(locale, project, paths).serialize(),
    })
Esempio n. 5
0
def delete_translation(request):
    """Delete given translation."""
    try:
        t = request.POST['translation']
        paths = request.POST.getlist('paths[]')
    except MultiValueDictKeyError as e:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    translation = get_object_or_404(Translation, pk=t)

    # Non-privileged users can only delete own unapproved translations
    if not request.user.can_translate(translation.locale, translation.entity.resource.project):
        if translation.user == request.user:
            if translation.approved is True:
                return HttpResponseForbidden(
                    "Forbidden: Can't delete approved translations"
                )
        else:
            return HttpResponseForbidden(
                "Forbidden: Can't delete translations from other users"
            )

    translation.delete()

    project = translation.entity.resource.project
    locale = translation.locale
    translations = Translation.for_locale_project_paths(locale, project, paths)

    return JsonResponse({
        'stats': TranslatedResource.objects.stats(project, paths, locale),
        'authors': translations.authors().serialize(),
        'counts_per_minute': translations.counts_per_minute(),
    })
Esempio n. 6
0
    def execute_create_db(self):
        new_entities = []

        for vcs_entity in self.changes['create_db']:
            # We can't use bulk_create since we need a PK
            entity, created = Entity.objects.get_or_create(
                **self.get_entity_updates(vcs_entity))

            if created:
                new_entities.append(entity)

            for locale_code, vcs_translation in vcs_entity.translations.items(
            ):
                for plural_form, string in vcs_translation.strings.items():
                    self.translations_to_create.append(
                        Translation(entity=entity,
                                    locale=self.locales[locale_code],
                                    string=string,
                                    plural_form=plural_form,
                                    approved=not vcs_translation.fuzzy,
                                    approved_date=self.now
                                    if not vcs_translation.fuzzy else None,
                                    fuzzy=vcs_translation.fuzzy))

        self.send_notifications(new_entities)
Esempio n. 7
0
def authors_and_time_range(request, locale, slug, part):
    locale = get_object_or_404(Locale, code=locale)
    project = get_object_or_404(Project.objects.available(), slug=slug)
    paths = [part] if part != 'all-resources' else None

    translations = Translation.for_locale_project_paths(locale, project, paths)

    return JsonResponse({
        'authors': translations.authors(),
        'counts_per_minute': translations.counts_per_minute(),
    }, safe=False)
Esempio n. 8
0
def authors_and_time_range(request, locale, slug, part):
    locale = get_object_or_404(Locale, code=locale)
    project = get_object_or_404(Project.objects.available(), slug=slug)
    paths = [part] if part != 'all-resources' else None

    translations = Translation.for_locale_project_paths(locale, project, paths)

    return JsonResponse({
        'authors': translations.authors().serialize(),
        'counts_per_minute': translations.counts_per_minute(),
    }, safe=False)
Esempio n. 9
0
    def execute_update_db(self):
        for locale_code, db_entity, vcs_entity in self.changes['update_db']:
            for field, value in self.get_entity_updates(vcs_entity).items():
                setattr(db_entity, field, value)

            if db_entity.is_dirty(check_relationship=True):
                self.entities_to_update.append(db_entity)

            # Update translations for the entity.
            vcs_translation = vcs_entity.translations[locale_code]
            db_translations = db_entity.translation_set.filter(
                locale__code=locale_code, )
            approved_translations = []

            for plural_form, string in vcs_translation.strings.items():
                # Check if we need to modify an existing translation or
                # create a new one.
                db_translation = match_attr(db_translations,
                                            plural_form=plural_form,
                                            string=string)
                if db_translation:
                    if not db_translation.approved:
                        db_translation.approved = True
                        db_translation.approved_date = self.now
                    db_translation.fuzzy = vcs_translation.fuzzy
                    db_translation.extra = vcs_translation.extra

                    if db_translation.is_dirty():
                        self.translations_to_update.append(db_translation)
                    if not db_translation.fuzzy:
                        approved_translations.append(db_translation)
                else:
                    self.translations_to_create.append(
                        Translation(entity=db_entity,
                                    locale=self.locales[locale_code],
                                    string=string,
                                    plural_form=plural_form,
                                    approved=not vcs_translation.fuzzy,
                                    approved_date=self.now
                                    if not vcs_translation.fuzzy else None,
                                    fuzzy=vcs_translation.fuzzy,
                                    extra=vcs_translation.extra))

            # Any existing translations that were not approved get unapproved.
            for translation in db_translations.filter(
                    approved_date__lte=self.now):
                if translation not in approved_translations:
                    translation.approved = False
                    translation.approved_user = None
                    translation.approved_date = None

                    if translation.is_dirty():
                        self.translations_to_update.append(translation)
Esempio n. 10
0
def _save_translation(entity, locale, string, plural_form=None, fuzzy=False):
    """Admin interface: save new or update existing translation in DB."""

    approved = not fuzzy

    # Update existing translation if different from repository
    try:
        t = Translation.objects.get(entity=entity, locale=locale,
                                    plural_form=plural_form, approved=True)
        if t.string != string or t.fuzzy != fuzzy:
            t.string = string
            t.user = None
            t.date = datetime.datetime.now()
            t.approved = approved
            t.fuzzy = fuzzy
            t.save()

    # Save new translation if it doesn's exist yet
    except Translation.DoesNotExist:
        t = Translation(
            entity=entity, locale=locale, string=string,
            plural_form=plural_form, date=datetime.datetime.now(),
            approved=approved, fuzzy=fuzzy)
        t.save()
Esempio n. 11
0
    def execute_create_db(self):
        for vcs_entity in self.changes['create_db']:
            entity = Entity(**self.get_entity_updates(vcs_entity))
            entity.save()  # We can't use bulk_create since we need a PK

            for locale_code, vcs_translation in vcs_entity.translations.items():
                for plural_form, string in vcs_translation.strings.items():
                    self.translations_to_create.append(Translation(
                        entity=entity,
                        locale=self.locales[locale_code],
                        string=string,
                        plural_form=plural_form,
                        approved=not vcs_translation.fuzzy,
                        approved_date=self.now if not vcs_translation.fuzzy else None,
                        fuzzy=vcs_translation.fuzzy
                    ))
Esempio n. 12
0
def _save_translation(entity, locale, translation, author=""):
    """Admin interface: save new or update existing translation in DB."""

    translations = Translation.objects.filter(entity=entity, locale=locale).order_by("date")

    if len(translations) == 0:  # New translation
        t = Translation(entity=entity, locale=locale, string=translation, author=author, date=datetime.datetime.now())
    else:  # Update translation
        t = translations.reverse()[0]
        t.string = translation
        t.author = author
        t.date = datetime.datetime.now()
    t.save()
Esempio n. 13
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),
        })
Esempio n. 14
0
def update_translation(request):
    """Update entity translation for the specified locale and user."""
    try:
        entity = request.POST["entity"]
        string = request.POST["translation"]
        locale = request.POST["locale"]
        plural_form = request.POST["plural_form"]
        original = request.POST["original"]
        ignore_check = request.POST["ignore_check"]
        approve = json.loads(request.POST["approve"])
        paths = request.POST.getlist("paths[]")
    except MultiValueDictKeyError as e:
        return HttpResponseBadRequest("Bad Request: {error}".format(error=e))

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        l = Locale.objects.get(code__iexact=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

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

    user = request.user
    if not request.user.is_authenticated():
        if e.resource.project.pk != 1:
            log.error("Not authenticated")
            return HttpResponse("error")
        else:
            user = None

    try:
        quality_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        quality_checks = True

    ignore = False
    if ignore_check == "true" or not quality_checks:
        ignore = True

    now = timezone.now()
    can_translate = request.user.has_perm("base.can_translate_locale", l) and (
        not request.user.profile.force_suggestions or approve
    )
    translations = Translation.objects.filter(entity=e, locale=l, plural_form=plural_form)

    # Newlines are not allowed in .lang files (bug 1190754)
    if e.resource.format == "lang" and "\n" in string:
        return HttpResponse("Newline characters are not allowed.")

    # Translations exist
    if len(translations) > 0:

        # Same translation exists
        try:
            t = translations.get(string=string)

            # If added by privileged user, approve and unfuzzy it
            if can_translate:

                # Unless there's nothing to be changed
                if t.user is not None and t.approved and t.approved_user and t.approved_date and not t.fuzzy:
                    return HttpResponse("Same translation already exists.")

                warnings = utils.quality_check(original, string, l, ignore)
                if warnings:
                    return warnings

                translations.update(approved=False, approved_user=None, approved_date=None)
                translations.update(fuzzy=False)

                if t.user is None:
                    t.user = user

                t.approved = True
                t.approved_date = timezone.now()
                t.fuzzy = False

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

                if request.user.is_authenticated():
                    t.save()

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

            # If added by non-privileged user, unfuzzy it
            else:
                if t.fuzzy:
                    warnings = utils.quality_check(original, string, l, ignore)
                    if warnings:
                        return warnings

                    if t.user is None:
                        t.user = user

                    t.approved = False
                    t.approved_user = None
                    t.approved_date = None
                    t.fuzzy = False

                    if request.user.is_authenticated():
                        t.save()

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

                return HttpResponse("Same translation already exists.")

        # Different translation added
        except:
            warnings = utils.quality_check(original, string, l, ignore)
            if warnings:
                return warnings

            if can_translate:
                translations.update(approved=False, approved_user=None, approved_date=None)

            translations.update(fuzzy=False)

            t = Translation(
                entity=e, locale=l, user=user, string=string, plural_form=plural_form, date=now, approved=can_translate
            )

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

            if request.user.is_authenticated():
                t.save()

            # Return active (approved or latest) translation
            try:
                active = translations.filter(approved=True).latest("date")
            except Translation.DoesNotExist:
                active = translations.latest("date")

            return JsonResponse(
                {
                    "type": "added",
                    "translation": active.serialize(),
                    "stats": TranslatedResource.objects.stats(e.resource.project, paths, l),
                }
            )

    # No translations saved yet
    else:
        warnings = utils.quality_check(original, string, l, ignore)
        if warnings:
            return warnings

        t = Translation(
            entity=e, locale=l, user=user, string=string, plural_form=plural_form, date=now, approved=can_translate
        )

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

        if request.user.is_authenticated():
            t.save()

        return JsonResponse(
            {
                "type": "saved",
                "translation": t.serialize(),
                "stats": TranslatedResource.objects.stats(e.resource.project, paths, l),
            }
        )
Esempio n. 15
0
    def test_manage_project_strings_download_csv(self):
        locale_kl = LocaleFactory.create(code='kl', name='Klingon')
        locale_gs = LocaleFactory.create(code='gs', name='Geonosian')
        project = ProjectFactory.create(data_source='database',
                                        locales=[locale_kl, locale_gs],
                                        repositories=[])

        url = reverse('pontoon.admin.project.strings', args=(project.slug, ))

        new_strings = """
             And on the pedestal these words appear:
            'My name is Ozymandias, king of kings:
            Look on my works, ye Mighty, and despair!'
        """
        response = self.client.post(url, {'new_strings': new_strings})
        assert_code(response, 200)

        # Test downloading the data.
        response = self.client.get(url, {'format': 'csv'})
        assert_code(response, 200)
        assert_equal(response._headers['content-type'],
                     ('Content-Type', 'text/csv'))

        # Verify the original content is here.
        assert_contains(response, 'pedestal')
        assert_contains(response, 'Ozymandias')
        assert_contains(response, 'Mighty')

        # Verify we have the locale columns.
        assert_contains(response, 'kl')
        assert_contains(response, 'gs')

        # Now add some translations.
        entity = Entity.objects.filter(
            string='And on the pedestal these words appear:')[0]
        Translation(
            string='Et sur le piédestal il y a ces mots :',
            entity=entity,
            locale=locale_kl,
            approved=True,
        ).save()
        Translation(
            string='Und auf dem Sockel steht die Schrift: ‚Mein Name',
            entity=entity,
            locale=locale_gs,
            approved=True,
        ).save()

        entity = Entity.objects.filter(
            string='\'My name is Ozymandias, king of kings:')[0]
        Translation(
            string='"Mon nom est Ozymandias, Roi des Rois.',
            entity=entity,
            locale=locale_kl,
            approved=True,
        ).save()
        Translation(
            string='Ist Osymandias, aller Kön’ge König: –',
            entity=entity,
            locale=locale_gs,
            approved=True,
        ).save()

        entity = Entity.objects.filter(
            string='Look on my works, ye Mighty, and despair!\'')[0]
        Translation(
            string='Voyez mon œuvre, vous puissants, et désespérez !"',
            entity=entity,
            locale=locale_kl,
            approved=True,
        ).save()
        Translation(
            string='Seht meine Werke, Mächt’ge, und erbebt!‘',
            entity=entity,
            locale=locale_gs,
            approved=True,
        ).save()

        response = self.client.get(url, {'format': 'csv'})

        # Verify the translated content is here.
        assert_contains(response, 'pedestal')
        assert_contains(response, 'piédestal')
        assert_contains(response, 'Sockel')

        assert_contains(response, 'Mighty')
        assert_contains(response, 'puissants')
        assert_contains(response, 'Mächt’ge')
Esempio n. 16
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),
    })
Esempio n. 17
0
def update_translation(request):
    """Update entity translation for the specified locale and user."""
    try:
        entity = request.POST['entity']
        string = request.POST['translation']
        locale = request.POST['locale']
        plural_form = request.POST['plural_form']
        original = request.POST['original']
        ignore_check = request.POST['ignore_check']
    except MultiValueDictKeyError as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        l = Locale.objects.get(code__iexact=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

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

    user = request.user
    if not request.user.is_authenticated():
        if e.resource.project.pk != 1:
            log.error("Not authenticated")
            return HttpResponse("error")
        else:
            user = None

    try:
        quality_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        quality_checks = True

    ignore = False
    if ignore_check == 'true' or not quality_checks:
        ignore = True

    now = timezone.now()
    can_translate = (
        request.user.has_perm('base.can_translate_locale', l)
        and not request.user.profile.force_suggestions
    )
    translations = Translation.objects.filter(
        entity=e, locale=l, plural_form=plural_form)

    # Newlines are not allowed in .lang files (bug 1190754)
    if e.resource.format == 'lang' and '\n' in string:
        return HttpResponse('Newline characters are not allowed.')

    # Translations exist
    if len(translations) > 0:

        # Same translation exists
        try:
            t = translations.get(string=string)

            # If added by privileged user, approve and unfuzzy it
            if can_translate:

                # Unless there's nothing to be changed
                if t.user is not None and t.approved and t.approved_user \
                        and t.approved_date and not t.fuzzy:
                    return HttpResponse("Same translation already exists.")

                warnings = utils.quality_check(original, string, l, ignore)
                if warnings:
                    return warnings

                unapprove(translations)
                unfuzzy(translations)

                if t.user is None:
                    t.user = user

                t.approved = True
                t.approved_date = timezone.now()
                t.fuzzy = False

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

                if request.user.is_authenticated():
                    t.save()

                return HttpResponse(json.dumps({
                    'type': 'updated',
                    'translation': t.serialize(),
                }), content_type='application/json')

            # If added by non-privileged user, unfuzzy it
            else:
                if t.fuzzy:
                    warnings = utils.quality_check(original, string, l, ignore)
                    if warnings:
                        return warnings

                    if t.user is None:
                        t.user = user

                    t.approved = False
                    t.approved_user = None
                    t.approved_date = None
                    t.fuzzy = False

                    if request.user.is_authenticated():
                        t.save()

                    return HttpResponse(json.dumps({
                        'type': 'updated',
                        'translation': t.serialize(),
                    }), content_type='application/json')

                return HttpResponse("Same translation already exists.")

        # Different translation added
        except:
            warnings = utils.quality_check(original, string, l, ignore)
            if warnings:
                return warnings

            if can_translate:
                unapprove(translations)

            unfuzzy(translations)

            t = Translation(
                entity=e, locale=l, user=user, string=string,
                plural_form=plural_form, date=now,
                approved=can_translate)

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

            if request.user.is_authenticated():
                t.save()

            active = get_translation(
                entity=e, locale=l, plural_form=plural_form)

            return HttpResponse(json.dumps({
                'type': 'added',
                'translation': active.serialize(),
            }), content_type='application/json')

    # No translations saved yet
    else:
        warnings = utils.quality_check(original, string, l, ignore)
        if warnings:
            return warnings

        t = Translation(
            entity=e, locale=l, user=user, string=string,
            plural_form=plural_form, date=now,
            approved=can_translate)

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

        if request.user.is_authenticated():
            t.save()

        return HttpResponse(json.dumps({
            'type': 'saved',
            'translation': t.serialize(),
        }), content_type='application/json')
Esempio n. 18
0
def update_translation(request):
    """Update entity translation for the specified locale and user."""

    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 HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        locale = Locale.objects.get(code=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    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 HttpResponseForbidden(
            "Forbidden: This string is in read-only mode"
        )

    try:
        use_ttk_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        use_ttk_checks = True

    # Disable checks for tutorial project.
    if project.slug == 'tutorial':
        use_ttk_checks = False

    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(
        '-approved', '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,
        })

    failed_checks = run_checks(
        e,
        locale.code,
        original,
        string,
        use_ttk_checks,
    )

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

    # Translations exist
    if len(translations) > 0:
        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):
                translations.update(
                    approved=False,
                    approved_user=None,
                    approved_date=None,
                    rejected=True,
                    rejected_user=request.user,
                    rejected_date=timezone.now(),
                    fuzzy=False,
                )

                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

            # If added by non-privileged user and fuzzy, unfuzzy it
            elif t.fuzzy:
                t.approved = False
                t.approved_user = None
                t.approved_date = None
                t.fuzzy = False

            t.save()

            t.warnings.all().delete()
            t.errors.all().delete()
            save_failed_checks(t, failed_checks)

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

        # Different translation added
        else:
            if can_translate:
                translations.update(approved=False, approved_user=None, approved_date=None)

            translations.update(fuzzy=False)

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

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

            t.save()
            save_failed_checks(t, failed_checks)

            # Return active (approved or latest) translation
            try:
                active = translations.filter(approved=True).latest("date")
            except Translation.DoesNotExist:
                active = translations.latest("date")

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

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

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

        t.save()
        save_failed_checks(t, failed_checks)

        return JsonResponse({
            'type': 'saved',
            'translation': t.serialize(),
            'stats': TranslatedResource.objects.stats(project, paths, locale),
        })
Esempio n. 19
0
    def update_entity_translations_from_vcs(
            self,
            db_entity,
            locale_code,
            vcs_translation,
            user=None,
            db_translations=None,
            db_translations_approved_before_sync=None):
        if db_translations is None:
            db_translations = db_entity.translation_set.filter(
                locale__code=locale_code, )

        if db_translations_approved_before_sync is None:
            db_translations_approved_before_sync = db_translations.filter(
                approved_date__lte=self.now)

        approved_translations = []
        fuzzy_translations = []

        for plural_form, string in vcs_translation.strings.items():
            db_translation = match_attr(db_translations,
                                        plural_form=plural_form,
                                        string=string)

            # Modify existing translation.
            if db_translation:
                if not db_translation.approved and not vcs_translation.fuzzy:
                    db_translation.approved = True
                    db_translation.approved_user = user
                    db_translation.approved_date = self.now
                db_translation.rejected = False
                db_translation.fuzzy = vcs_translation.fuzzy
                db_translation.extra = vcs_translation.extra

                if db_translation.is_dirty():
                    self.translations_to_update[
                        db_translation.pk] = db_translation
                if db_translation.fuzzy:
                    fuzzy_translations.append(db_translation)
                else:
                    approved_translations.append(db_translation)

            # Create new translation.
            else:
                self.translations_to_create.append(
                    Translation(entity=db_entity,
                                locale=self.locales[locale_code],
                                string=string,
                                plural_form=plural_form,
                                approved=not vcs_translation.fuzzy,
                                approved_user=user,
                                approved_date=self.now
                                if not vcs_translation.fuzzy else None,
                                user=user,
                                fuzzy=vcs_translation.fuzzy,
                                extra=vcs_translation.extra))

        # Unapprove translations that were approved before the sync job started unless sync
        # resolves them as active approved translations.
        # Note: If translations get approved after the sync starts, duplicate approvals can arise.
        # We take care of that at the and of the sync job in tasks.py.
        for translation in db_translations_approved_before_sync:
            if translation not in approved_translations:
                # Use the translation instance already set for update if it exists.
                translation = self.translations_to_update.get(
                    translation.pk, translation)
                translation.approved = False
                translation.approved_user = None
                translation.approved_date = None

                # Reject translations unless they became fuzzy during sync. Condition is sufficient
                # because they were approved previously.
                if not translation.fuzzy:
                    translation.rejected = True
                    translation.rejected_user = user
                    translation.rejected_date = self.now

                if translation.is_dirty():
                    self.translations_to_update[translation.pk] = translation

        # Unfuzzy existing translations unless sync resolves them as active fuzzy translations.
        # Note: Translations cannot get fuzzy after the sync job starts, because they cannot be
        # made fuzzy in Pontoon.
        for translation in db_translations:
            if translation not in fuzzy_translations:
                # Use the translation instance already set for update if it exists.
                translation = self.translations_to_update.get(
                    translation.pk, translation)
                translation.fuzzy = False

                if translation.is_dirty():
                    self.translations_to_update[translation.pk] = translation
Esempio n. 20
0
def update_translation(request):
    """Update entity translation for the specified locale and user."""

    try:
        entity = request.POST['entity']
        string = request.POST['translation']
        locale = request.POST['locale']
        plural_form = request.POST['plural_form']
        original = request.POST['original']
        ignore_check = request.POST['ignore_check']
        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 HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        l = Locale.objects.get(code=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

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

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

    try:
        quality_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        quality_checks = True

    ignore = False
    if ignore_check == 'true' or not quality_checks:
        ignore = True

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

    # Newlines are not allowed in .lang files (bug 1190754)
    if e.resource.format == 'lang' and '\n' in string:
        return HttpResponse('Newline characters are not allowed.')

    # Translations exist
    if len(translations) > 0:

        # Same translation exists
        same_translations = translations.filter(string=string).order_by('-approved', '-date')
        if len(same_translations) > 0:
            t = same_translations[0]

            # If added by privileged user, approve and unfuzzy it
            if can_translate:

                # Unless there's nothing to be changed
                if t.user is not None and t.approved and t.approved_user \
                        and t.approved_date and not t.fuzzy:
                    return JsonResponse({
                        'same': True,
                        'message': 'Same translation already exists.',
                    })

                warnings = utils.quality_check(original, string, l, ignore)
                if warnings:
                    return warnings

                translations.update(approved=False, approved_user=None, approved_date=None)
                translations.update(fuzzy=False)

                if t.user is None:
                    t.user = user

                t.approved = True
                t.approved_date = timezone.now()
                t.fuzzy = False

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

                t.save()

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

            # If added by non-privileged user, unfuzzy it
            else:
                if t.fuzzy:
                    warnings = utils.quality_check(original, string, l, ignore)
                    if warnings:
                        return warnings

                    if t.user is None:
                        t.user = user

                    t.approved = False
                    t.approved_user = None
                    t.approved_date = None
                    t.fuzzy = False

                    t.save()

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

                return JsonResponse({
                    'same': True,
                    'message': 'Same translation already exists.',
                })

        # Different translation added
        else:
            warnings = utils.quality_check(original, string, l, ignore)
            if warnings:
                return warnings

            if can_translate:
                translations.update(approved=False, approved_user=None, approved_date=None)

            translations.update(fuzzy=False)

            t = Translation(
                entity=e, locale=l, user=user, string=string,
                plural_form=plural_form, date=now,
                approved=can_translate)

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

            t.save()

            # Return active (approved or latest) translation
            try:
                active = translations.filter(approved=True).latest("date")
            except Translation.DoesNotExist:
                active = translations.latest("date")

            return JsonResponse({
                'type': 'added',
                'translation': active.serialize(),
                'stats': TranslatedResource.objects.stats(project, paths, l),
            })

    # No translations saved yet
    else:
        warnings = utils.quality_check(original, string, l, ignore)
        if warnings:
            return warnings

        t = Translation(
            entity=e, locale=l, user=user, string=string,
            plural_form=plural_form, date=now,
            approved=can_translate)

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

        t.save()

        return JsonResponse({
            'type': 'saved',
            'translation': t.serialize(),
            'stats': TranslatedResource.objects.stats(project, paths, l),
        })
Esempio n. 21
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 HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        locale = Locale.objects.get(code=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    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 HttpResponseForbidden(
            "Forbidden: This string is in read-only mode"
        )

    try:
        use_ttk_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        use_ttk_checks = True

    # Disable checks for tutorial project.
    if project.slug == 'tutorial':
        use_ttk_checks = False

    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,
        })

    failed_checks = run_checks(
        e,
        locale.code,
        original,
        string,
        use_ttk_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()

                t.warnings.all().delete()
                t.errors.all().delete()
                save_failed_checks(t, failed_checks)

                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()
            save_failed_checks(t, failed_checks)

            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()
        save_failed_checks(t, failed_checks)

        return JsonResponse({
            'type': 'saved',
            'translation': t.serialize(),
            'stats': TranslatedResource.objects.stats(project, paths, locale),
        })
Esempio n. 22
0
    def update_entity_translations_from_vcs(self,
                                            db_entity,
                                            locale_code,
                                            vcs_translation,
                                            user=None,
                                            db_translations=None,
                                            old_translations=None):
        if db_translations is None:
            db_translations = db_entity.translation_set.filter(
                locale__code=locale_code, )
        approved_translations = []
        fuzzy_translations = []

        for plural_form, string in vcs_translation.strings.items():
            # Check if we need to modify an existing translation or
            # create a new one.
            db_translation = match_attr(db_translations,
                                        plural_form=plural_form,
                                        string=string)
            if db_translation:
                if not db_translation.approved and not vcs_translation.fuzzy:
                    db_translation.approved = True
                    db_translation.approved_date = self.now
                    db_translation.approved_user = user
                db_translation.fuzzy = vcs_translation.fuzzy
                db_translation.extra = vcs_translation.extra

                if db_translation.is_dirty():
                    self.translations_to_update.append(db_translation)
                if not db_translation.fuzzy:
                    approved_translations.append(db_translation)
                else:
                    fuzzy_translations.append(db_translation)
            else:
                self.translations_to_create.append(
                    Translation(entity=db_entity,
                                locale=self.locales[locale_code],
                                string=string,
                                plural_form=plural_form,
                                approved=not vcs_translation.fuzzy,
                                approved_date=self.now
                                if not vcs_translation.fuzzy else None,
                                approved_user=user,
                                user=user,
                                fuzzy=vcs_translation.fuzzy,
                                extra=vcs_translation.extra))

        # Any existing translations that were not approved get unapproved.
        if old_translations is None:
            old_translations = db_translations.filter(
                approved_date__lte=self.now)

        for translation in old_translations:
            if translation not in approved_translations:
                translation.approved = False
                translation.approved_user = None
                translation.approved_date = None

                if translation.is_dirty():
                    self.translations_to_update.append(translation)

        # Any existing translations that are no longer fuzzy get unfuzzied.
        for translation in db_translations:
            if translation not in fuzzy_translations:
                translation.fuzzy = False

                if translation.is_dirty():
                    self.translations_to_update.append(translation)
Esempio n. 23
0
def update_translation(request, template=None):
    """Update entity translation for the specified locale and user."""
    log.debug("Update entity translation for the specified locale and user.")

    if not request.is_ajax():
        log.error("Non-AJAX request")
        raise Http404

    if request.method != 'POST':
        log.error("Non-POST request")
        raise Http404

    try:
        entity = request.POST['entity']
        string = request.POST['translation']
        locale = request.POST['locale']
        plural_form = request.POST['plural_form']
        original = request.POST['original']
        ignore_check = request.POST['ignore_check']
    except MultiValueDictKeyError as error:
        log.error(str(error))
        return HttpResponse("error")

    log.debug("Entity: " + entity)
    log.debug("Translation: " + string)
    log.debug("Locale: " + locale)

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        l = Locale.objects.get(code__iexact=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

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

    user = request.user
    if not request.user.is_authenticated():
        if e.resource.project.pk != 1:
            log.error("Not authenticated")
            return HttpResponse("error")
        else:
            user = None

    try:
        quality_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        quality_checks = True

    ignore = False
    if ignore_check == 'true' or not quality_checks:
        ignore = True

    now = timezone.now()
    can_localize = request.user.has_perm('base.can_localize')
    translations = Translation.objects.filter(entity=e,
                                              locale=l,
                                              plural_form=plural_form)

    # Newlines are not allowed in .lang files (bug 1190754)
    if e.resource.format == 'lang' and '\n' in string:
        return HttpResponse('Newline characters are not allowed.')

    # Translations exist
    if len(translations) > 0:

        # Same translation exists
        try:
            t = translations.get(string=string)

            # If added by privileged user, approve and unfuzzy it
            if can_localize:

                # Unless there's nothing to be changed
                if t.user is not None and t.approved and t.approved_user \
                        and t.approved_date and not t.fuzzy:
                    return HttpResponse("Same translation already exists.")

                warnings = utils.quality_check(original, string, l, ignore)
                if warnings:
                    return warnings

                unapprove(translations)
                unfuzzy(translations)

                if t.user is None:
                    t.user = user

                t.approved = True
                t.approved_date = timezone.now()
                t.fuzzy = False

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

                if request.user.is_authenticated():
                    t.save()

                return HttpResponse(json.dumps({
                    'type': 'updated',
                    'translation': t.serialize(),
                }),
                                    content_type='application/json')

            # If added by non-privileged user, unfuzzy it
            else:
                if t.fuzzy:
                    warnings = utils.quality_check(original, string, l, ignore)
                    if warnings:
                        return warnings

                    if t.user is None:
                        t.user = user

                    t.approved = False
                    t.approved_user = None
                    t.approved_date = None
                    t.fuzzy = False

                    if request.user.is_authenticated():
                        t.save()

                    return HttpResponse(json.dumps({
                        'type':
                        'updated',
                        'translation':
                        t.serialize(),
                    }),
                                        content_type='application/json')

                return HttpResponse("Same translation already exists.")

        # Different translation added
        except:
            warnings = utils.quality_check(original, string, l, ignore)
            if warnings:
                return warnings

            if can_localize:
                unapprove(translations)

            unfuzzy(translations)

            t = Translation(entity=e,
                            locale=l,
                            user=user,
                            string=string,
                            plural_form=plural_form,
                            date=now,
                            approved=can_localize)

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

            if request.user.is_authenticated():
                t.save()

            active = get_translation(entity=e,
                                     locale=l,
                                     plural_form=plural_form)

            return HttpResponse(json.dumps({
                'type': 'added',
                'translation': active.serialize(),
            }),
                                content_type='application/json')

    # No translations saved yet
    else:
        warnings = utils.quality_check(original, string, l, ignore)
        if warnings:
            return warnings

        t = Translation(entity=e,
                        locale=l,
                        user=user,
                        string=string,
                        plural_form=plural_form,
                        date=now,
                        approved=can_localize)

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

        if request.user.is_authenticated():
            t.save()

        return HttpResponse(json.dumps({
            'type': 'saved',
            'translation': t.serialize(),
        }),
                            content_type='application/json')
Esempio n. 24
0
def entities(request):
    """Get entities for the specified project, locale and paths."""
    try:
        project = request.POST['project']
        locale = request.POST['locale']
        paths = request.POST.getlist('paths[]')
        limit = int(request.POST.get('limit', 50))
    except (MultiValueDictKeyError, ValueError) as err:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=err))

    project = get_object_or_404(Project, slug=project)
    locale = get_object_or_404(Locale, code__iexact=locale)

    status = request.POST.get('status', '')
    extra = request.POST.get('extra', '')
    time = request.POST.get('time', '')
    author = request.POST.get('author', '')
    search = request.POST.get('search', '')
    exclude_entities = split_ints(request.POST.get('excludeEntities', ''))

    # Only return entities with provided IDs (batch editing)
    entity_ids = split_ints(request.POST.get('entityIds', ''))

    if entity_ids:
        entities = (
            Entity.objects.filter(pk__in=entity_ids)
            .prefetch_resources_translations(locale)
            .distinct()
            .order_by('order')
        )
        translations = Translation.for_locale_project_paths(locale, project, paths)

        return JsonResponse({
            'entities': Entity.map_entities(locale, entities),
            'stats': TranslatedResource.objects.stats(project, paths, locale),
            'authors': translations.authors().serialize(),
            'counts_per_minute': translations.counts_per_minute(),
        }, safe=False)

    entities = Entity.for_project_locale(
        project, locale, paths, status, search, exclude_entities, extra, time, author
    )

    # Only return a list of entity PKs (batch editing: select all)
    if request.POST.get('pkOnly', None):
        return JsonResponse({
            'entity_pks': list(entities.values_list('pk', flat=True)),
        })

    visible_entities = []

    # In-place view: load all entities
    if request.POST.get('inplaceEditor', None):
        has_next = False
        entities_to_map = Entity.for_project_locale(
            project, locale, paths, None, None, exclude_entities
        )
        visible_entities = entities.values_list('pk', flat=True)

    # Out-of-context view: paginate entities
    else:
        paginator = Paginator(entities, limit)

        try:
            entities_page = paginator.page(1)
        except EmptyPage:
            return JsonResponse({
                'has_next': False,
                'stats': {},
                'authors': [],
                'counts_per_minute': [],
            })

        has_next = entities_page.has_next()
        entities_to_map = entities_page.object_list

        # If requested entity not on the first page
        entity = request.POST.get('entity', None)
        if entity:
            try:
                entity_pk = int(entity)
            except ValueError as err:
                return HttpResponseBadRequest('Bad Request: {error}'.format(error=err))

            # TODO: entities_to_map.values_list() doesn't return entities from selected page
            if entity_pk not in [e.pk for e in entities_to_map]:
                if entity_pk in entities.values_list('pk', flat=True):
                    entities_to_map = list(entities_to_map) + list(entities.filter(pk=entity_pk))

    translations = Translation.for_locale_project_paths(locale, project, paths)

    return JsonResponse({
        'entities': Entity.map_entities(locale, entities_to_map, visible_entities),
        'has_next': has_next,
        'stats': TranslatedResource.objects.stats(project, paths, locale),
        'authors': translations.authors().serialize(),
        'counts_per_minute': translations.counts_per_minute(),
    }, safe=False)
Esempio n. 25
0
        t = Translation.objects.get(entity=e, locale=l, author=request.user.email)

        if translation != '':
            log.debug("Translation updated.")
            t.string = translation
            t.save()
            return HttpResponse("updated")
        else:
            log.debug("Translation deleted.")
            t.delete()
            return HttpResponse("deleted")

    except Translation.DoesNotExist:
        """Save new translation."""
        if translation != '':
            t = Translation(entity=e, locale=l, string=translation, 
                author=request.user.email, date=datetime.datetime.now())
            t.save()
            log.debug("Translation saved.")
            return HttpResponse("saved")
        else:
            log.debug("Translation not set.")
            return HttpResponse("not set")

def _generate_properties_content(url, locale):
    """
    Generate .properties file content.

    Args:
        pid: project ID
        locale: locale code
    Returns:
Esempio n. 26
0
def pretranslate(project, locales=None, entities=None):
    """
    Identifies strings without any translations and any suggestions.
    Engages TheAlgorithm (bug 1552796) to gather pretranslations.
    Stores pretranslations as suggestions (approved=False) to DB.

    :arg Project project: the project to be pretranslated
    :arg Queryset locales: the locales for the project to be pretranslated
    :arg Queryset entites: the entities for the project to be pretranslated

    :returns: None
    """

    log.info("Fetching pretranslations for project {} started".format(
        project.name))

    if locales:
        locales = project.locales.filter(pk__in=locales)
    else:
        locales = project.locales

    locales = (locales.filter(project_locale__readonly=False).distinct().
               prefetch_project_locale(project))

    if not entities:
        entities = Entity.objects.filter(
            resource__project=project,
            obsolete=False,
        ).prefetch_related("resource")

    # get available TranslatedResource pairs
    tr_pairs = (TranslatedResource.objects.filter(
        resource__project=project,
        locale__in=locales,
    ).annotate(locale_resource=Concat(
        "locale_id", V("-"), "resource_id",
        output_field=CharField())).values_list("locale_resource",
                                               flat=True).distinct())

    # Fetch all distinct locale-entity pairs for which translation exists
    translated_entities = (Translation.objects.filter(
        locale__in=locales,
        entity__in=entities,
    ).annotate(locale_entity=Concat(
        "locale_id", V("-"), "entity_id",
        output_field=CharField())).values_list("locale_entity",
                                               flat=True).distinct())

    translated_entities = list(translated_entities)

    translations = []

    # To keep track of changed Locales and TranslatedResources
    # Also, their latest_translation and stats count
    locale_dict = {}
    tr_dict = {}

    tr_filter = []
    index = -1

    for locale in locales:
        log.info("Fetching pretranslations for locale {} started".format(
            locale.code))
        for entity in entities:
            locale_entity = "{}-{}".format(locale.id, entity.id)
            locale_resource = "{}-{}".format(locale.id, entity.resource.id)
            if locale_entity in translated_entities or locale_resource not in tr_pairs:
                continue

            strings = get_translations(entity, locale)

            if not strings:
                continue

            for string, plural_form, user in strings:
                t = Translation(
                    entity=entity,
                    locale=locale,
                    string=string,
                    user=user,
                    approved=False,
                    active=True,
                    plural_form=plural_form,
                )

                translations.append(t)

                index += 1

                if locale_resource not in tr_dict:
                    tr_dict[locale_resource] = [index, 0]
                    # Add query for fetching respective TranslatedResource.
                    tr_filter.append(
                        Q(locale__id=locale.id)
                        & Q(resource__id=entity.resource.id))

                if locale.code not in locale_dict:
                    locale_dict[locale.code] = [locale, index, 0]

                # Update the latest translation index
                tr_dict[locale_resource][0] = index
                locale_dict[locale.code][1] = index

                # Increment number of translations (used to adjust stats)
                tr_dict[locale_resource][1] += 1
                locale_dict[locale.code][2] += 1
        log.info("Fetching pretranslations for locale {} done".format(
            locale.code))

    if len(translations) == 0:
        return

    translations = Translation.objects.bulk_create(translations)

    # Update latest activity and unreviewed count for the project.
    project.latest_translation = translations[-1]
    project.unreviewed_strings += len(translations)
    project.save(update_fields=["latest_translation", "unreviewed_strings"])

    # Update latest activity and unreviewed count for changed instances.
    update_changed_instances(tr_filter, tr_dict, locale_dict, translations)

    log.info("Fetching pretranslations for project {} done".format(
        project.name))
Esempio n. 27
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'

    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)

                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)

            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)

        return JsonResponse({
            'type':
            'saved',
            'translation':
            t.serialize(),
            'stats':
            TranslatedResource.objects.stats(project, paths, locale),
        })
Esempio n. 28
0
def update_translation(request, template=None):
    """Update entity translation for the specified locale and user."""
    log.debug("Update entity translation for the specified locale and user.")

    if not request.is_ajax():
        log.error("Non-AJAX request")
        raise Http404

    if request.method != "POST":
        log.error("Non-POST request")
        raise Http404

    try:
        entity = request.POST["entity"]
        string = request.POST["translation"]
        locale = request.POST["locale"]
        plural_form = request.POST["plural_form"]
        original = request.POST["original"]
        ignore_check = request.POST["ignore_check"]
    except MultiValueDictKeyError as error:
        log.error(str(error))
        return HttpResponse("error")

    log.debug("Entity: " + entity)
    log.debug("Translation: " + string)
    log.debug("Locale: " + locale)

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        l = Locale.objects.get(code__iexact=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

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

    user = request.user
    if not request.user.is_authenticated():
        if e.resource.project.pk != 1:
            log.error("Not authenticated")
            return HttpResponse("error")
        else:
            user = None

    try:
        quality_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        quality_checks = True

    ignore = False
    if ignore_check == "true" or not quality_checks:
        ignore = True

    now = timezone.now()
    can_localize = request.user.has_perm("base.can_localize")
    translations = Translation.objects.filter(entity=e, locale=l, plural_form=plural_form)

    # Newlines are not allowed in .lang files (bug 1190754)
    if e.resource.format == "lang" and "\n" in string:
        return HttpResponse("Newline characters are not allowed.")

    # Translations exist
    if len(translations) > 0:

        # Same translation exists
        try:
            t = translations.get(string=string)

            # If added by privileged user, approve and unfuzzy it
            if can_localize:

                # Unless there's nothing to be changed
                if t.user is not None and t.approved and t.approved_user and t.approved_date and not t.fuzzy:
                    return HttpResponse("Same translation already exists.")

                warnings = utils.quality_check(original, string, l, ignore)
                if warnings:
                    return warnings

                unapprove(translations)
                unfuzzy(translations)

                if t.user is None:
                    t.user = user

                t.approved = True
                t.approved_date = timezone.now()
                t.fuzzy = False

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

                if request.user.is_authenticated():
                    t.save()

                return HttpResponse(
                    json.dumps({"type": "updated", "translation": t.serialize()}), content_type="application/json"
                )

            # If added by non-privileged user, unfuzzy it
            else:
                if t.fuzzy:
                    warnings = utils.quality_check(original, string, l, ignore)
                    if warnings:
                        return warnings

                    if t.user is None:
                        t.user = user

                    t.approved = False
                    t.approved_user = None
                    t.approved_date = None
                    t.fuzzy = False

                    if request.user.is_authenticated():
                        t.save()

                    return HttpResponse(
                        json.dumps({"type": "updated", "translation": t.serialize()}), content_type="application/json"
                    )

                return HttpResponse("Same translation already exists.")

        # Different translation added
        except:
            warnings = utils.quality_check(original, string, l, ignore)
            if warnings:
                return warnings

            if can_localize:
                unapprove(translations)

            unfuzzy(translations)

            t = Translation(
                entity=e, locale=l, user=user, string=string, plural_form=plural_form, date=now, approved=can_localize
            )

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

            if request.user.is_authenticated():
                t.save()

            active = get_translation(entity=e, locale=l, plural_form=plural_form)

            return HttpResponse(
                json.dumps({"type": "added", "translation": active.serialize()}), content_type="application/json"
            )

    # No translations saved yet
    else:
        warnings = utils.quality_check(original, string, l, ignore)
        if warnings:
            return warnings

        t = Translation(
            entity=e, locale=l, user=user, string=string, plural_form=plural_form, date=now, approved=can_localize
        )

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

        if request.user.is_authenticated():
            t.save()

        return HttpResponse(
            json.dumps({"type": "saved", "translation": t.serialize()}), content_type="application/json"
        )
Esempio n. 29
0
def dump_po(project, locale, relative_path):
    """Dump .po (gettext) file with relative path from database."""

    locale_directory_path = get_locale_directory(project, locale)["path"]
    path = os.path.join(locale_directory_path, relative_path)
    po = polib.pofile(path)
    date = timezone.make_aware(datetime.min)
    newest = Translation()
    resource = Resource.objects.filter(project=project, path=relative_path)
    entities = Entity.objects.filter(resource=resource, obsolete=False)

    for entity in entities:
        entry = po.find(entity.string)
        if entry:
            if not entry.msgid_plural:
                try:
                    translation = Translation.objects.filter(
                        entity=entity, locale=locale, approved=True) \
                        .latest('date')
                    entry.msgstr = translation.string

                    if translation.date > date:
                        date = translation.date
                        newest = translation
                    if 'fuzzy' in entry.flags:
                        entry.flags.remove('fuzzy')

                except Translation.DoesNotExist as e:
                    pass

            else:
                for i in range(0, 6):
                    if i < (locale.nplurals or 1):
                        try:
                            translation = Translation.objects.filter(
                                entity=entity, locale=locale,
                                plural_form=i, approved=True).latest('date')
                            entry.msgstr_plural[i] = translation.string

                            if translation.date > date:
                                date = translation.date
                                newest = translation
                            if 'fuzzy' in entry.flags:
                                entry.flags.remove('fuzzy')

                        except Translation.DoesNotExist as e:
                            pass

                    # Remove obsolete plural forms if exist
                    else:
                        if i in entry.msgstr_plural:
                            del entry.msgstr_plural[i]

    # Update PO metadata
    if newest.id:
        if newest.user:
            po.metadata['PO-Revision-Date'] = newest.date
            po.metadata['Last-Translator'] = '%s <%s>' \
                % (newest.user.first_name, newest.user.email)
    po.metadata['Language'] = locale.code.replace('-', '_')
    po.metadata['X-Generator'] = 'Pontoon'

    if locale.nplurals:
        po.metadata['Plural-Forms'] = 'nplurals=%s; plural=%s;' \
            % (str(locale.nplurals), locale.plural_rule)

    po.save()
    log.debug("File updated: " + path)
Esempio n. 30
0
def pretranslate(self, project_pk, locales=None, entities=None):
    """
    Identifies strings without any translations and any suggestions.
    Engages TheAlgorithm (bug 1552796) to gather pretranslations.
    Stores pretranslations as suggestions (approved=False) to DB.

    :arg project_pk: the pk of the project to be pretranslated
    :arg Queryset locales: the locales for the project to be pretranslated
    :arg Queryset entites: the entities for the project to be pretranslated

    :returns: None
    """
    project = Project.objects.get(pk=project_pk)

    log.info("Fetching pretranslations for project {} started".format(
        project.name))

    if locales:
        locales = project.locales.filter(pk__in=locales)
    else:
        locales = project.locales

    locales = (locales.filter(project_locale__readonly=False).distinct().
               prefetch_project_locale(project))

    if not entities:
        entities = Entity.objects.filter(
            resource__project=project,
            obsolete=False,
        ).prefetch_related("resource")

    # get available TranslatedResource pairs
    tr_pairs = (TranslatedResource.objects.filter(
        resource__project=project,
        locale__in=locales,
    ).annotate(locale_resource=Concat(
        "locale_id", V("-"), "resource_id",
        output_field=CharField())).values_list("locale_resource",
                                               flat=True).distinct())

    # Fetch all distinct locale-entity pairs for which translation exists
    translated_entities = (Translation.objects.filter(
        locale__in=locales,
        entity__in=entities,
    ).annotate(locale_entity=Concat(
        "locale_id", V("-"), "entity_id",
        output_field=CharField())).values_list("locale_entity",
                                               flat=True).distinct())

    translated_entities = list(translated_entities)

    translations = []

    # To keep track of changed TranslatedResources and their latest_translation
    tr_dict = {}

    tr_filter = []
    index = -1

    for locale in locales:
        log.info("Fetching pretranslations for locale {} started".format(
            locale.code))
        for entity in entities:
            locale_entity = "{}-{}".format(locale.id, entity.id)
            locale_resource = "{}-{}".format(locale.id, entity.resource.id)
            if locale_entity in translated_entities or locale_resource not in tr_pairs:
                continue

            strings = get_translations(entity, locale)

            if not strings:
                continue

            for string, plural_form, user in strings:
                t = Translation(
                    entity=entity,
                    locale=locale,
                    string=string,
                    user=user,
                    approved=False,
                    fuzzy=True,
                    active=True,
                    plural_form=plural_form,
                )

                index += 1
                translations.append(t)

                if locale_resource not in tr_dict:
                    tr_dict[locale_resource] = index

                    # Add query for fetching respective TranslatedResource.
                    tr_filter.append(
                        Q(locale__id=locale.id)
                        & Q(resource__id=entity.resource.id))

                # Update the latest translation index
                tr_dict[locale_resource] = index

        log.info("Fetching pretranslations for locale {} done".format(
            locale.code))

    if len(translations) == 0:
        return

    translations = Translation.objects.bulk_create(translations)

    # Run checks on all translations
    translation_pks = {translation.pk for translation in translations}
    bulk_run_checks(
        Translation.objects.for_checks().filter(pk__in=translation_pks))

    # Mark translations as changed
    changed_entities = {}
    existing = ChangedEntityLocale.objects.values_list("entity",
                                                       "locale").distinct()
    for t in translations:
        key = (t.entity.pk, t.locale.pk)
        # Remove duplicate changes to prevent unique constraint violation
        if key not in existing:
            changed_entities[key] = ChangedEntityLocale(entity=t.entity,
                                                        locale=t.locale)
    ChangedEntityLocale.objects.bulk_create(changed_entities.values())

    # Update latest activity and stats for changed instances.
    update_changed_instances(tr_filter, tr_dict, translations)

    log.info("Fetching pretranslations for project {} done".format(
        project.name))
Esempio n. 31
0
def entities(request):
    """Get entities for the specified project, locale and paths."""
    try:
        project = request.POST['project']
        locale = request.POST['locale']
        paths = request.POST.getlist('paths[]')
        limit = int(request.POST.get('limit', 50))
    except (MultiValueDictKeyError, ValueError) as err:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=err))

    project = get_object_or_404(Project, slug=project)
    locale = get_object_or_404(Locale, code__iexact=locale)

    filter_type = request.POST.get('filter', '')
    search = request.POST.get('search', '')
    exclude_entities = request.POST.getlist('excludeEntities[]', [])

    # Only return entities with provided IDs (batch editing)
    entity_ids = request.POST.getlist('entityIds[]', [])
    if entity_ids:
        entities = (
            Entity.objects.filter(pk__in=entity_ids)
            .prefetch_resources_translations(locale)
            .distinct()
            .order_by('order')
        )

        return JsonResponse({
            'entities': Entity.map_entities(locale, entities),
            'stats': TranslatedResource.objects.stats(project, paths, locale),
            'authors': Translation.authors(locale, project, paths).serialize(),
        }, safe=False)

    entities = Entity.for_project_locale(
        project, locale, paths, filter_type, search, exclude_entities
    )

    # Only return a list of entity PKs (batch editing: select all)
    if request.POST.get('pkOnly', None):
        return JsonResponse({
            'entity_pks': list(entities.values_list('pk', flat=True)),
        })

    visible_entities = []

    # In-place view: load all entities
    if request.POST.get('inplaceEditor', None):
        has_next = False
        entities_to_map = Entity.for_project_locale(
            project, locale, paths, None, None, exclude_entities
        )
        visible_entities = entities.values_list('pk', flat=True)

    # Out-of-context view: paginate entities
    else:
        paginator = Paginator(entities, limit)

        try:
            entities_page = paginator.page(1)
        except EmptyPage:
            return JsonResponse({
                'has_next': False,
                'stats': {},
                'authors': []
            })

        has_next = entities_page.has_next()
        entities_to_map = entities_page.object_list

        # If requested entity not on the first page
        entity = request.POST.get('entity', None)
        if entity:
            try:
                entity_pk = int(entity)
            except ValueError as err:
                return HttpResponseBadRequest('Bad Request: {error}'.format(error=err))

            # TODO: entities_to_map.values_list() doesn't return entities from selected page
            if entity_pk not in [e.pk for e in entities_to_map]:
                if entity_pk in entities.values_list('pk', flat=True):
                    entities_to_map = list(entities_to_map) + list(entities.filter(pk=entity_pk))

    return JsonResponse({
        'entities': Entity.map_entities(locale, entities_to_map, visible_entities),
        'has_next': has_next,
        'stats': TranslatedResource.objects.stats(project, paths, locale),
        'authors': Translation.authors(locale, project, paths).serialize(),
    }, safe=False)
Esempio n. 32
0
def update_translation(request, template=None):
    """Update entity translation for the specified locale and user."""
    log.debug("Update entity translation for the specified locale and user.")

    if not request.is_ajax():
        log.error("Non-AJAX request")
        raise Http404

    if request.method != 'POST':
        log.error("Non-POST request")
        raise Http404

    try:
        entity = request.POST['entity']
        string = request.POST['translation']
        locale = request.POST['locale']
        plural_form = request.POST['plural_form']
        original = request.POST['original']
        ignore_check = request.POST['ignore_check']
    except MultiValueDictKeyError as error:
        log.error(str(error))
        return HttpResponse("error")

    log.debug("Entity: " + entity)
    log.debug("Translation: " + string)
    log.debug("Locale: " + locale)

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        l = Locale.objects.get(code=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

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

    user = request.user
    if not request.user.is_authenticated():
        if e.resource.project.pk != 1:
            log.error("Not authenticated")
            return HttpResponse("error")
        else:
            user = None

    try:
        quality_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        quality_checks = True

    ignore = False
    if ignore_check == 'true' or not quality_checks:
        ignore = True

    now = datetime.datetime.now()
    can_localize = request.user.has_perm('base.can_localize')
    translations = Translation.objects.filter(
        entity=e, locale=l, plural_form=plural_form)

    # Translations exist
    if len(translations) > 0:

        # Same translation exists
        try:
            t = translations.get(string__iexact=string)

            # If added by privileged user, approve and unfuzzy it
            if can_localize:

                # Unless there's nothing to be changed
                if t.user is not None and t.approved and t.approved_user \
                        and t.approved_date and not t.fuzzy:
                    return HttpResponse("Same translation already exist.")

                warnings = utils.quality_check(original, string, l, ignore)
                if warnings:
                    return warnings

                unapprove(translations)
                unfuzzy(translations)

                if t.user is None:
                    t.user = user

                t.approved = True
                t.fuzzy = False

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

                if request.user.is_authenticated():
                    t.save()

                return HttpResponse(json.dumps({
                    'type': 'updated',
                    'translation': t.serialize(),
                }), mimetype='application/json')

            # If added by non-privileged user, unfuzzy it
            else:
                if t.fuzzy:
                    warnings = utils.quality_check(original, string, l, ignore)
                    if warnings:
                        return warnings

                    if t.user is None:
                        t.user = user

                    t.approved = False
                    t.approved_user = None
                    t.approved_date = None
                    t.fuzzy = False

                    if request.user.is_authenticated():
                        t.save()

                    return HttpResponse(json.dumps({
                        'type': 'updated',
                        'translation': t.serialize(),
                    }), mimetype='application/json')

                return HttpResponse("Same translation already exist.")

        # Different translation added
        except:
            warnings = utils.quality_check(original, string, l, ignore)
            if warnings:
                return warnings

            if can_localize:
                unapprove(translations)

            unfuzzy(translations)

            t = Translation(
                entity=e, locale=l, user=user, string=string,
                plural_form=plural_form, date=now,
                approved=can_localize)

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

            if request.user.is_authenticated():
                t.save()

            active = get_translation(
                entity=e, locale=l, plural_form=plural_form)

            return HttpResponse(json.dumps({
                'type': 'added',
                'translation': active.serialize(),
            }), mimetype='application/json')

    # No translations saved yet
    else:
        warnings = utils.quality_check(original, string, l, ignore)
        if warnings:
            return warnings

        t = Translation(
            entity=e, locale=l, user=user, string=string,
            plural_form=plural_form, date=now,
            approved=can_localize)

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

        if request.user.is_authenticated():
            t.save()

        return HttpResponse(json.dumps({
            'type': 'saved',
            'translation': t.serialize(),
        }), mimetype='application/json')
Esempio n. 33
0
def update_translation(request):
    """Update entity translation for the specified locale and user."""
    try:
        entity = request.POST['entity']
        string = request.POST['translation']
        locale = request.POST['locale']
        plural_form = request.POST['plural_form']
        original = request.POST['original']
        ignore_check = request.POST['ignore_check']
        approve = json.loads(request.POST['approve'])
        paths = request.POST.getlist('paths[]')
    except MultiValueDictKeyError as e:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=e))

    try:
        e = Entity.objects.get(pk=entity)
    except Entity.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

    try:
        l = Locale.objects.get(code__iexact=locale)
    except Locale.DoesNotExist as error:
        log.error(str(error))
        return HttpResponse("error")

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

    user = request.user
    project = e.resource.project
    if not request.user.is_authenticated():
        if project.pk != 1:
            log.error("Not authenticated")
            return HttpResponse("error")
        else:
            user = None

    try:
        quality_checks = UserProfile.objects.get(user=user).quality_checks
    except UserProfile.DoesNotExist as error:
        quality_checks = True

    ignore = False
    if ignore_check == 'true' or not quality_checks:
        ignore = True

    now = timezone.now()
    can_translate = (
        request.user.has_perm('base.can_translate_locale', l)
        and (not request.user.profile.force_suggestions or approve)
    )
    translations = Translation.objects.filter(
        entity=e, locale=l, plural_form=plural_form)

    # Newlines are not allowed in .lang files (bug 1190754)
    if e.resource.format == 'lang' and '\n' in string:
        return HttpResponse('Newline characters are not allowed.')

    # Translations exist
    if len(translations) > 0:

        # Same translation exists
        try:
            t = translations.get(string=string)

            # If added by privileged user, approve and unfuzzy it
            if can_translate:

                # Unless there's nothing to be changed
                if t.user is not None and t.approved and t.approved_user \
                        and t.approved_date and not t.fuzzy:
                    return JsonResponse({
                        'same': True,
                        'message': 'Same translation already exists.',
                    })

                warnings = utils.quality_check(original, string, l, ignore)
                if warnings:
                    return warnings

                translations.update(approved=False, approved_user=None, approved_date=None)
                translations.update(fuzzy=False)

                if t.user is None:
                    t.user = user

                t.approved = True
                t.approved_date = timezone.now()
                t.fuzzy = False

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

                if request.user.is_authenticated():
                    t.save()

                return JsonResponse({
                    'type': 'updated',
                    'translation': t.serialize(),
                    'stats': TranslatedResource.objects.stats(project, paths, l),
                    'authors': Translation.authors(l, project, paths).serialize(),
                })

            # If added by non-privileged user, unfuzzy it
            else:
                if t.fuzzy:
                    warnings = utils.quality_check(original, string, l, ignore)
                    if warnings:
                        return warnings

                    if t.user is None:
                        t.user = user

                    t.approved = False
                    t.approved_user = None
                    t.approved_date = None
                    t.fuzzy = False

                    if request.user.is_authenticated():
                        t.save()

                    return JsonResponse({
                        'type': 'updated',
                        'translation': t.serialize(),
                        'stats': TranslatedResource.objects.stats(project, paths, l),
                        'authors': Translation.authors(l, project, paths).serialize(),
                    })

                return JsonResponse({
                    'same': True,
                    'message': 'Same translation already exists.',
                })

        # Different translation added
        except:
            warnings = utils.quality_check(original, string, l, ignore)
            if warnings:
                return warnings

            if can_translate:
                translations.update(approved=False, approved_user=None, approved_date=None)

            translations.update(fuzzy=False)

            t = Translation(
                entity=e, locale=l, user=user, string=string,
                plural_form=plural_form, date=now,
                approved=can_translate)

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

            if request.user.is_authenticated():
                t.save()

            # Return active (approved or latest) translation
            try:
                active = translations.filter(approved=True).latest("date")
            except Translation.DoesNotExist:
                active = translations.latest("date")

            return JsonResponse({
                'type': 'added',
                'translation': active.serialize(),
                'stats': TranslatedResource.objects.stats(project, paths, l),
                'authors': Translation.authors(l, project, paths).serialize(),
            })

    # No translations saved yet
    else:
        warnings = utils.quality_check(original, string, l, ignore)
        if warnings:
            return warnings

        t = Translation(
            entity=e, locale=l, user=user, string=string,
            plural_form=plural_form, date=now,
            approved=can_translate)

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

        if request.user.is_authenticated():
            t.save()

        return JsonResponse({
            'type': 'saved',
            'translation': t.serialize(),
            'stats': TranslatedResource.objects.stats(project, paths, l),
            'authors': Translation.authors(l, project, paths).serialize(),
        })
Esempio n. 34
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),
        }
    )