Beispiel #1
0
def explorer_breadcrumb(
    context,
    page,
    page_perms=None,
    include_self=True,
    use_next_template=False,
    trailing_breadcrumb_title=None,
):
    user = context["request"].user

    # find the closest common ancestor of the pages that this user has direct explore permission
    # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb
    cca = get_explorable_root_page(user)
    if not cca:
        return {"pages": Page.objects.none()}

    return {
        "pages": page.get_ancestors(inclusive=include_self)
        .descendant_of(cca, inclusive=True)
        .specific(),
        "current_page": page,
        "page_perms": page_perms,
        "use_next_template": use_next_template,
        "trailing_breadcrumb_title": trailing_breadcrumb_title,  # Only used in collapsible breadcrumb templates
    }
Beispiel #2
0
    def get_context_data(self, **kwargs):
        request = self.request

        panels = [
            SiteSummaryPanel(request),
            UpgradeNotificationPanel(request),
            PagesForModerationPanel(request),
            RecentEditsPanel(request),
            RecentActivityPanel(request)
        ]

        for fn in hooks.get_hooks('construct_homepage_panels'):
            fn(request, panels)

        root_page = get_explorable_root_page(request.user)
        if root_page:
            root_site = root_page.get_site()
        else:
            root_site = None

        real_site_name = None
        if root_site:
            real_site_name = root_site.site_name if root_site.site_name else root_site.hostname
        return {
            'root_page': root_page,
            'root_site': root_site,
            'site_name': real_site_name if real_site_name else settings.WAGTAIL_SITE_NAME,
            'panels': sorted(panels, key=lambda p: p.order),
            'user': request.user
        }
Beispiel #3
0
    def render_component(self, request):
        start_page = get_explorable_root_page(request.user)

        if start_page:
            return PageExplorerMenuItemComponent(self.name, self.label, self.url, start_page.id, icon_name=self.icon_name, classnames=self.classnames)
        else:
            return super().render_component(request)
Beispiel #4
0
def explorer_breadcrumb(
    context,
    page,
    page_perms=None,
    include_self=True,
    trailing_arrow=False,
    show_header_buttons=False,
):
    user = context["request"].user

    # find the closest common ancestor of the pages that this user has direct explore permission
    # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb
    cca = get_explorable_root_page(user)
    if not cca:
        return {"pages": Page.objects.none()}

    return {
        "pages":
        page.get_ancestors(inclusive=include_self).descendant_of(
            cca, inclusive=True).specific(),
        "current_page":
        page,
        "page_perms":
        page_perms,
        "trailing_arrow":
        trailing_arrow,
        "show_header_buttons":
        show_header_buttons,
    }
Beispiel #5
0
    def get_context(self):
        root_page = get_explorable_root_page(self.request.user)

        if root_page:
            page_count = Page.objects.descendant_of(root_page,
                                                    inclusive=True).count()

            if root_page.is_root():
                # If the root page the user has access to is the Wagtail root,
                # subtract one from this count because the root is not a real page.
                page_count -= 1

                # If precisely one site exists, link to its homepage rather than the
                # tree root, to discourage people from trying to create pages as siblings
                # of the homepage (#1883)
                try:
                    root_page = Site.objects.get().root_page
                except (Site.DoesNotExist, Site.MultipleObjectsReturned):
                    pass
        else:
            page_count = 0

        return {
            'root_page': root_page,
            'total_pages': page_count,
        }
Beispiel #6
0
def home(request):

    panels = [
        SiteSummaryPanel(request),
        UpgradeNotificationPanel(request),
        PagesForModerationPanel(request),
        RecentEditsPanel(request),
    ]

    for fn in hooks.get_hooks('construct_homepage_panels'):
        fn(request, panels)

    root_page = get_explorable_root_page(request.user)
    if root_page:
        root_site = root_page.get_site()
    else:
        root_site = None

    real_site_name = None
    if root_site:
        real_site_name = root_site.site_name if root_site.site_name else root_site.hostname

    return render(request, "wagtailadmin/home.html", {
        'root_page': root_page,
        'root_site': root_site,
        'site_name': real_site_name if real_site_name else settings.WAGTAIL_SITE_NAME,
        'panels': sorted(panels, key=lambda p: p.order),
        'user': request.user
    })
Beispiel #7
0
    def get_context(self, request):
        context = super().get_context(request)
        start_page = get_explorable_root_page(request.user)

        if start_page:
            context['start_page_id'] = start_page.id

        return context
 def test_nonadmin_sees_pages_below_closest_common_ancestor(self):
     User = get_user_model()
     user = User.objects.get(username='******')
     # Josh has permissions for /example-home/content/page-1 and /example-home/other-content,
     # of which the closest common ancestor is /example-home.
     self.assertEqual(get_explorable_root_page(user).id, 4)
     for page in get_pages_with_direct_explore_permission(user):
         self.assertIn(page.id, [6, 8])
Beispiel #9
0
 def test_nonadmin_sees_pages_below_closest_common_ancestor(self):
     User = get_user_model()
     user = User.objects.get(email="*****@*****.**")
     # Josh has permissions for /example-home/content/page-1 and /example-home/other-content,
     # of which the closest common ancestor is /example-home.
     self.assertEqual(get_explorable_root_page(user).id, 4)
     for page in get_pages_with_direct_explore_permission(user):
         self.assertIn(page.id, [6, 8])
Beispiel #10
0
    def get_context(self, request):
        context = super().get_context(request)
        start_page = get_explorable_root_page(request.user)

        if start_page:
            context['start_page_id'] = start_page.id

        return context
 def test_nonadmin_sees_only_explorable_pages(self):
     # Sam has permissions for /home and /example-home/content/page-1 , of which the closest
     # common ancestor is root; we don't show root in the menu, so the top level will consist
     # of 'home' and 'example-home' (but not the sibling 'home-2', which Sam doesn't have
     # permission on)
     User = get_user_model()
     user = User.objects.get(username='******')
     self.assertEqual(get_explorable_root_page(user).id, 1)
     for page in get_pages_with_direct_explore_permission(user):
         self.assertIn(page.id, [2, 6])
Beispiel #12
0
 def test_nonadmin_sees_only_explorable_pages(self):
     # Sam has permissions for /home and /example-home/content/page-1 , of which the closest
     # common ancestor is root; we don't show root in the menu, so the top level will consist
     # of 'home' and 'example-home' (but not the sibling 'home-2', which Sam doesn't have
     # permission on)
     User = get_user_model()
     user = User.objects.get(email="*****@*****.**")
     self.assertEqual(get_explorable_root_page(user).id, 1)
     for page in get_pages_with_direct_explore_permission(user):
         self.assertIn(page.id, [2, 6])
Beispiel #13
0
def move_breadcrumb(context, page_to_move, viewed_page):
    user = context["request"].user
    cca = get_explorable_root_page(user)
    if not cca:
        return {"pages": Page.objects.none()}

    return {
        "pages": viewed_page.get_ancestors(inclusive=True)
        .descendant_of(cca, inclusive=True)
        .specific(),
        "page_to_move_id": page_to_move.id,
    }
Beispiel #14
0
def explorer_breadcrumb(context, page, include_self=False):
    user = context['request'].user

    # find the closest common ancestor of the pages that this user has direct explore permission
    # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb
    cca = get_explorable_root_page(user)
    if not cca:
        return {'pages': Page.objects.none()}

    return {
        'pages': page.get_ancestors(inclusive=include_self).descendant_of(cca, inclusive=True).specific()
    }
Beispiel #15
0
def explorer_breadcrumb(context, page, include_self=False):
    user = context['request'].user

    # find the closest common ancestor of the pages that this user has direct explore permission
    # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb
    cca = get_explorable_root_page(user)
    if not cca:
        return {'pages': Page.objects.none()}

    return {
        'pages': page.get_ancestors(inclusive=include_self).descendant_of(cca, inclusive=True).specific()
    }
Beispiel #16
0
def get_site_for_user(user):
    root_page = get_explorable_root_page(user)
    if root_page:
        root_site = root_page.get_site()
    else:
        root_site = None
    real_site_name = None
    if root_site:
        real_site_name = root_site.site_name if root_site.site_name else root_site.hostname
    return {
        'root_page': root_page,
        'root_site': root_site,
        'site_name': real_site_name if real_site_name else settings.WAGTAIL_SITE_NAME,
    }
Beispiel #17
0
    def test_page_move_default_destination(self):
        response = self.client.get(
            reverse('wagtailadmin_pages:move', args=(self.test_page_b.id, )))
        self.assertEqual(response.status_code, 200)
        # The default destination is the parent of the page being moved
        self.assertEqual(response.context["viewed_page"].specific,
                         self.section_c)

        cca = get_explorable_root_page(self.user)
        destinations = self.section_c.get_ancestors().descendant_of(cca)
        self.assertTrue(destinations.exists())

        for destination_page in destinations:
            move_url = reverse('wagtailadmin_pages:move_choose_destination',
                               args=(self.test_page_b.id, destination_page.id))
            self.assertContains(response, move_url)
Beispiel #18
0
    def get_context(self):
        # If there is a single site, link to the homepage of that site
        # Otherwise, if there are multiple sites, link to the root page
        try:
            site = Site.objects.get()
            #root = site.root_page
            root = get_explorable_root_page(self.request.user)
            single_site = True
            total_pages = UserPagePermissionsProxy(
                self.request.user).editable_pages().count()
        except (Site.DoesNotExist, Site.MultipleObjectsReturned):
            root = None
            single_site = False

        return {
            'single_site': single_site,
            'root_page': root,
            #'total_pages': Page.objects.count() - 1,  # subtract 1 because the root node is not a real page
            'total_pages': total_pages,
        }
Beispiel #19
0
 def test_admins_see_all_pages(self):
     User = get_user_model()
     user = User.objects.get(username='******')
     self.assertEqual(get_explorable_root_page(user).id, 1)
Beispiel #20
0
def index(request, parent_page_id=None):
    if parent_page_id:
        parent_page = get_object_or_404(Page, id=parent_page_id)
    else:
        parent_page = Page.get_first_root_node()

    # This will always succeed because of the @user_passes_test above.
    root_page = get_explorable_root_page(request.user)

    # If this page isn't a descendant of the user's explorable root page,
    # then redirect to that explorable root page instead.
    if not (parent_page.pk == root_page.pk
            or parent_page.is_descendant_of(root_page)):
        return redirect("wagtailadmin_explore", root_page.pk)

    parent_page = parent_page.specific

    user_perms = UserPagePermissionsProxy(request.user)
    pages = (parent_page.get_children().prefetch_related(
        "content_type", "sites_rooted_here")
             & user_perms.explorable_pages())

    # Get page ordering
    ordering = request.GET.get("ordering", "-latest_revision_created_at")
    if ordering not in [
            "title",
            "-title",
            "content_type",
            "-content_type",
            "live",
            "-live",
            "latest_revision_created_at",
            "-latest_revision_created_at",
            "ord",
    ]:
        ordering = "-latest_revision_created_at"

    if ordering == "ord":
        # preserve the native ordering from get_children()
        pass
    elif ordering == "latest_revision_created_at":
        # order by oldest revision first.
        # Special case NULL entries - these should go at the top of the list.
        # Do this by annotating with Count('latest_revision_created_at'),
        # which returns 0 for these
        pages = pages.annotate(
            null_position=Count("latest_revision_created_at")).order_by(
                "null_position", "latest_revision_created_at")
    elif ordering == "-latest_revision_created_at":
        # order by oldest revision first.
        # Special case NULL entries - these should go at the end of the list.
        pages = pages.annotate(
            null_position=Count("latest_revision_created_at")).order_by(
                "-null_position", "-latest_revision_created_at")
    else:
        pages = pages.order_by(ordering)

    # Don't paginate if sorting by page order - all pages must be shown to
    # allow drag-and-drop reordering
    do_paginate = ordering != "ord"

    # We want specific page instances, but do not need streamfield values here
    pages = pages.defer_streamfields().specific()

    # allow hooks defer_streamfieldsyset
    for hook in hooks.get_hooks("construct_explorer_page_queryset"):
        pages = hook(parent_page, pages, request)

    # Annotate queryset with various states to be used later for performance optimisations
    if getattr(settings, "WAGTAIL_WORKFLOW_ENABLED", True):
        pages = pages.prefetch_workflow_states()

    pages = pages.annotate_site_root_state().annotate_approved_schedule()

    # Pagination
    if do_paginate:
        paginator = Paginator(pages, per_page=50)
        pages = paginator.get_page(request.GET.get("p"))

    show_ordering_column = request.GET.get("ordering") == "ord"

    context = {
        "parent_page": parent_page.specific,
        "ordering": ordering,
        "pages": pages,
        "do_paginate": do_paginate,
        "locale": None,
        "translations": [],
        "show_ordering_column": show_ordering_column,
        "show_bulk_actions": not show_ordering_column,
        "show_locale_labels": False,
    }

    if getattr(settings, "WAGTAIL_I18N_ENABLED", False):
        if not parent_page.is_root():
            context.update({
                "locale":
                parent_page.locale,
                "translations": [{
                    "locale":
                    translation.locale,
                    "url":
                    reverse("wagtailadmin_explore", args=[translation.id]),
                } for translation in parent_page.get_translations().only(
                    "id", "locale").select_related("locale")],
            })
        else:
            context["show_locale_labels"] = True

    return TemplateResponse(request, "wagtailadmin/pages/index.html", context)
Beispiel #21
0
 def test_nonadmin_sees_leaf_page_at_root_level(self):
     User = get_user_model()
     user = User.objects.get(username='******')
     self.assertEqual(get_explorable_root_page(user).id, 6)
Beispiel #22
0
 def test_nav_root_for_nonadmin_is_closest_common_ancestor(self):
     User = get_user_model()
     user = User.objects.get(username='******')
     self.assertEqual(get_explorable_root_page(user).id, 2)
 def test_nav_root_for_nonadmin_is_closest_common_ancestor(self):
     User = get_user_model()
     user = User.objects.get(username='******')
     self.assertEqual(get_explorable_root_page(user).id, 2)
Beispiel #24
0
 def test_nonadmin_with_no_page_perms_cannot_explore(self):
     User = get_user_model()
     user = User.objects.get(email="*****@*****.**")
     self.assertIsNone(get_explorable_root_page(user))
Beispiel #25
0
 def test_nonadmin_sees_leaf_page_at_root_level(self):
     User = get_user_model()
     user = User.objects.get(email="*****@*****.**")
     self.assertEqual(get_explorable_root_page(user).id, 6)
 def test_nonadmin_sees_leaf_page_at_root_level(self):
     User = get_user_model()
     user = User.objects.get(username='******')
     self.assertEqual(get_explorable_root_page(user).id, 6)
Beispiel #27
0
 def test_nav_root_for_nonadmin_is_closest_common_ancestor(self):
     User = get_user_model()
     user = User.objects.get(email="*****@*****.**")
     self.assertEqual(get_explorable_root_page(user).id, 2)
 def test_admins_see_all_pages(self):
     User = get_user_model()
     user = User.objects.get(username='******')
     self.assertEqual(get_explorable_root_page(user).id, 1)
 def test_nonadmin_with_no_page_perms_cannot_explore(self):
     User = get_user_model()
     user = User.objects.get(username='******')
     self.assertEqual(get_explorable_root_page(user), None)
Beispiel #30
0
def index(request, parent_page_id=None):
    if parent_page_id:
        parent_page = get_object_or_404(Page, id=parent_page_id)
    else:
        parent_page = Page.get_first_root_node()

    # This will always succeed because of the @user_passes_test above.
    root_page = get_explorable_root_page(request.user)

    # If this page isn't a descendant of the user's explorable root page,
    # then redirect to that explorable root page instead.
    if not (
        parent_page.pk == root_page.pk or
        parent_page.is_descendant_of(root_page)
    ):
        return redirect('wagtailadmin_explore', root_page.pk)

    parent_page = parent_page.specific

    pages = parent_page.get_children().prefetch_related('content_type', 'sites_rooted_here')

    # Get page ordering
    ordering = request.GET.get('ordering', '-latest_revision_created_at')
    if ordering not in [
        'title',
        '-title',
        'content_type',
        '-content_type',
        'live', '-live',
        'latest_revision_created_at',
        '-latest_revision_created_at',
        'ord'
    ]:
        ordering = '-latest_revision_created_at'

    if ordering == 'ord':
        # preserve the native ordering from get_children()
        pass
    elif ordering == 'latest_revision_created_at':
        # order by oldest revision first.
        # Special case NULL entries - these should go at the top of the list.
        # Do this by annotating with Count('latest_revision_created_at'),
        # which returns 0 for these
        pages = pages.annotate(
            null_position=Count('latest_revision_created_at')
        ).order_by('null_position', 'latest_revision_created_at')
    elif ordering == '-latest_revision_created_at':
        # order by oldest revision first.
        # Special case NULL entries - these should go at the end of the list.
        pages = pages.annotate(
            null_position=Count('latest_revision_created_at')
        ).order_by('-null_position', '-latest_revision_created_at')
    else:
        pages = pages.order_by(ordering)

    # Don't paginate if sorting by page order - all pages must be shown to
    # allow drag-and-drop reordering
    do_paginate = ordering != 'ord'

    if do_paginate:
        # Retrieve pages in their most specific form.
        # Only do this for paginated listings, as this could potentially be a
        # very expensive operation when performed on a large queryset.
        pages = pages.specific()

    # allow hooks to modify the queryset
    for hook in hooks.get_hooks('construct_explorer_page_queryset'):
        pages = hook(parent_page, pages, request)

    # Pagination
    if do_paginate:
        paginator, pages = paginate(request, pages, per_page=50)

    return render(request, 'wagtailadmin/pages/index.html', {
        'parent_page': parent_page.specific,
        'ordering': ordering,
        'pagination_query_params': "ordering=%s" % ordering,
        'pages': pages,
        'do_paginate': do_paginate,
    })
Beispiel #31
0
 def test_nonadmin_with_no_page_perms_cannot_explore(self):
     User = get_user_model()
     user = User.objects.get(username='******')
     self.assertEqual(get_explorable_root_page(user), None)
Beispiel #32
0
 def get_root_page(self):
     return get_explorable_root_page(self.request.user)
Beispiel #33
0
 def test_admins_see_all_pages(self):
     User = get_user_model()
     user = User.objects.get(email="*****@*****.**")
     self.assertEqual(get_explorable_root_page(user).id, 1)
Beispiel #34
0
def edit_translation(request, translation, instance):
    if isinstance(instance, Page):
        # Page
        # Note: Edit permission is already checked by the edit page view

        page_perms = instance.permissions_for_user(request.user)

        is_live = instance.live
        is_locked = instance.locked

        if instance.live_revision:
            last_published_at = instance.live_revision.created_at
            last_published_by = instance.live_revision.user
        else:
            last_published_at = instance.last_published_at
            last_published_by = None

        if instance.live:
            live_url = instance.full_url
        else:
            live_url = None

        can_publish = page_perms.can_publish()
        can_unpublish = page_perms.can_unpublish()
        can_lock = page_perms.can_lock()
        can_unlock = page_perms.can_unlock()
        can_delete = page_perms.can_delete()

    else:
        # Snippet
        # Note: Edit permission is already checked by the edit snippet view

        is_live = True
        is_locked = False
        last_published_at = None
        last_published_by = None
        live_url = None

        can_publish = True
        can_unpublish = False
        can_lock = False
        can_unlock = False
        can_delete = request.user.has_perm(
            get_permission_name('delete', instance.__class__))

    source_instance = translation.source.get_source_instance()

    if request.method == 'POST':
        if request.POST.get('action') == 'publish':
            if isinstance(instance, Page):
                if not page_perms.can_publish():
                    raise PermissionDenied

            try:
                translation.save_target(user=request.user, publish=True)

            except ValidationError:
                messages.error(
                    request,
                    _("New validation errors were found when publishing '{object}' in {locale}. Please fix them or click publish again to ignore these translations for now."
                      ).format(
                          object=str(instance),
                          locale=translation.target_locale.get_display_name()))

            else:
                # Refresh instance to title in success message is up to date
                instance.refresh_from_db()

                string_segments = translation.source.stringsegment_set.all(
                ).order_by('order')
                string_translations = string_segments.get_translations(
                    translation.target_locale)

                # Using annotate_translation as this ignores errors by default (so both errors and missing segments treated the same)
                if string_segments.annotate_translation(
                        translation.target_locale).filter(
                            translation__isnull=True).exists():
                    # One or more strings had an error
                    messages.warning(
                        request,
                        _("Published '{object}' in {locale} with missing translations - see below."
                          ).format(object=str(instance),
                                   locale=translation.target_locale.
                                   get_display_name()))

                else:
                    messages.success(
                        request,
                        _("Published '{object}' in {locale}.").format(
                            object=str(instance),
                            locale=translation.target_locale.get_display_name(
                            )))

        return redirect(request.path)

    string_segments = translation.source.stringsegment_set.all().order_by(
        'order')
    string_translations = string_segments.get_translations(
        translation.target_locale)

    overridable_segments = translation.source.overridablesegment_set.all(
    ).order_by('order')
    segment_overrides = overridable_segments.get_overrides(
        translation.target_locale)
    related_object_segments = translation.source.relatedobjectsegment_set.all(
    ).order_by('order')

    tab_helper = TabHelper(source_instance)

    breadcrumb = []
    title_segment_id = None
    if isinstance(instance, Page):
        # find the closest common ancestor of the pages that this user has direct explore permission
        # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb
        cca = get_explorable_root_page(request.user)
        if cca:
            breadcrumb = [{
                'id':
                page.id,
                'isRoot':
                page.is_root(),
                'title':
                page.title,
                'exploreUrl':
                reverse('wagtailadmin_explore_root') if page.is_root() else
                reverse('wagtailadmin_explore', args=[page.id]),
            } for page in instance.get_ancestors(
                inclusive=False).descendant_of(cca, inclusive=True)]

        # Set to the ID of a string segment that represents the title.
        # If this segment has a translation, the title will be replaced with that translation.
        try:
            title_segment_id = string_segments.get(context__path='title').id
        except StringSegment.DoesNotExist:
            pass

    machine_translator = None
    translator = get_machine_translator()
    if translator and translator.can_translate(translation.source.locale,
                                               translation.target_locale):
        machine_translator = {
            'name':
            translator.display_name,
            'url':
            reverse('wagtail_localize:machine_translate',
                    args=[translation.id]),
        }

    string_segment_data = [{
        'type':
        'string',
        'id':
        segment.id,
        'contentPath':
        segment.context.path,
        'source':
        segment.string.data,
        'location':
        get_segment_location_info(source_instance, tab_helper,
                                  segment.context.path),
        'editUrl':
        reverse('wagtail_localize:edit_string_translation',
                kwargs={
                    'translation_id': translation.id,
                    'string_segment_id': segment.id
                }),
        'order':
        segment.order,
    } for segment in string_segments]
    syncronised_value_segment_data = [{
        'type':
        'synchronised_value',
        'id':
        segment.id,
        'contentPath':
        segment.context.path,
        'location':
        get_segment_location_info(source_instance,
                                  tab_helper,
                                  segment.context.path,
                                  widget=True),
        'value':
        segment.data,
        'editUrl':
        reverse('wagtail_localize:edit_override',
                kwargs={
                    'translation_id': translation.id,
                    'overridable_segment_id': segment.id
                }),
        'order':
        segment.order,
    } for segment in overridable_segments]

    def get_source_object_info(segment):
        instance = segment.get_source_instance()

        if isinstance(instance, Page):
            return {
                'title':
                str(instance),
                'isLive':
                instance.live,
                'liveUrl':
                instance.full_url,
                'editUrl':
                reverse('wagtailadmin_pages:edit', args=[instance.id]),
                'createTranslationRequestUrl':
                reverse('wagtail_localize:submit_page_translation',
                        args=[instance.id]),
            }

        else:
            return {
                'title':
                str(instance),
                'isLive':
                True,
                'editUrl':
                reverse('wagtailsnippets:edit',
                        args=[
                            instance._meta.app_label,
                            instance._meta.model_name,
                            quote(instance.id)
                        ]),
                'createTranslationRequestUrl':
                reverse('wagtail_localize:submit_snippet_translation',
                        args=[
                            instance._meta.app_label,
                            instance._meta.model_name,
                            quote(instance.id)
                        ]),
            }

    def get_dest_object_info(segment):
        instance = segment.object.get_instance_or_none(
            translation.target_locale)
        if not instance:
            return

        if isinstance(instance, Page):
            return {
                'title': str(instance),
                'isLive': instance.live,
                'liveUrl': instance.full_url,
                'editUrl': reverse('wagtailadmin_pages:edit',
                                   args=[instance.id]),
            }

        else:
            return {
                'title':
                str(instance),
                'isLive':
                True,
                'editUrl':
                reverse('wagtailsnippets:edit',
                        args=[
                            instance._meta.app_label,
                            instance._meta.model_name,
                            quote(instance.id)
                        ]),
            }

    def get_translation_progress(segment, locale):
        try:
            translation = Translation.objects.get(
                source__object_id=segment.object_id,
                target_locale=locale,
                enabled=True)

        except Translation.DoesNotExist:
            return None

        total_segments, translated_segments = translation.get_progress()

        return {
            'totalSegments': total_segments,
            'translatedSegments': translated_segments,
        }

    related_object_segment_data = [{
        'type':
        'related_object',
        'id':
        segment.id,
        'contentPath':
        segment.context.path,
        'location':
        get_segment_location_info(source_instance, tab_helper,
                                  segment.context.path),
        'order':
        segment.order,
        'source':
        get_source_object_info(segment),
        'dest':
        get_dest_object_info(segment),
        'translationProgress':
        get_translation_progress(segment, translation.target_locale),
    } for segment in related_object_segments]

    segments = string_segment_data + syncronised_value_segment_data + related_object_segment_data
    segments.sort(key=lambda segment: segment['order'])

    return render(
        request,
        'wagtail_localize/admin/edit_translation.html',
        {
            # These props are passed directly to the TranslationEditor react component
            'props':
            json.dumps(
                {
                    'object': {
                        'title':
                        str(instance),
                        'titleSegmentId':
                        title_segment_id,
                        'isLive':
                        is_live,
                        'isLocked':
                        is_locked,
                        'lastPublishedDate':
                        last_published_at.strftime(DATE_FORMAT)
                        if last_published_at is not None else None,
                        'lastPublishedBy':
                        UserSerializer(last_published_by).data
                        if last_published_by is not None else None,
                        'liveUrl':
                        live_url,
                    },
                    'breadcrumb':
                    breadcrumb,
                    'tabs':
                    tab_helper.tabs_with_slugs,
                    'sourceLocale': {
                        'code':
                        translation.source.locale.language_code,
                        'displayName':
                        translation.source.locale.get_display_name(),
                    },
                    'locale': {
                        'code':
                        translation.target_locale.language_code,
                        'displayName':
                        translation.target_locale.get_display_name(),
                    },
                    'translations': [{
                        'title':
                        str(translated_instance),
                        'locale': {
                            'code':
                            translated_instance.locale.language_code,
                            'displayName':
                            translated_instance.locale.get_display_name(),
                        },
                        'editUrl':
                        reverse('wagtailadmin_pages:edit',
                                args=[translated_instance.id]) if isinstance(
                                    translated_instance, Page) else
                        reverse('wagtailsnippets:edit',
                                args=[
                                    translated_instance._meta.app_label,
                                    translated_instance._meta.model_name,
                                    quote(translated_instance.id)
                                ]),
                    } for translated_instance in instance.get_translations().
                                     select_related('locale')],
                    'perms': {
                        'canPublish': can_publish,
                        'canUnpublish': can_unpublish,
                        'canLock': can_lock,
                        'canUnlock': can_unlock,
                        'canDelete': can_delete,
                    },
                    'links': {
                        'downloadPofile':
                        reverse('wagtail_localize:download_pofile',
                                args=[translation.id]),
                        'uploadPofile':
                        reverse('wagtail_localize:upload_pofile',
                                args=[translation.id]),
                        'unpublishUrl':
                        reverse('wagtailadmin_pages:unpublish',
                                args=[instance.id]) if isinstance(
                                    instance, Page) else None,
                        'lockUrl':
                        reverse('wagtailadmin_pages:lock', args=[instance.id])
                        if isinstance(instance, Page) else None,
                        'unlockUrl':
                        reverse('wagtailadmin_pages:unlock',
                                args=[instance.id]) if isinstance(
                                    instance, Page) else None,
                        'deleteUrl':
                        reverse('wagtailadmin_pages:delete',
                                args=[instance.id]) if isinstance(
                                    instance, Page) else reverse(
                                        'wagtailsnippets:delete',
                                        args=[
                                            instance._meta.app_label,
                                            instance._meta.model_name,
                                            quote(instance.pk)
                                        ]),
                        'stopTranslationUrl':
                        reverse('wagtail_localize:stop_translation',
                                args=[translation.id]),
                    },
                    'previewModes': [{
                        'mode':
                        mode,
                        'label':
                        label,
                        'url':
                        reverse('wagtail_localize:preview_translation',
                                args=[translation.id])
                        if mode == instance.default_preview_mode else reverse(
                            'wagtail_localize:preview_translation',
                            args=[translation.id, mode]),
                    } for mode, label in (instance.preview_modes if isinstance(
                        instance, Page) else [])],
                    'machineTranslator':
                    machine_translator,
                    'segments':
                    segments,

                    # We serialize the translation data using Django REST Framework.
                    # This gives us a consistent representation with the APIs so we
                    # can dynamically update translations in the view.
                    'initialStringTranslations':
                    StringTranslationSerializer(string_translations,
                                                many=True,
                                                context={
                                                    'translation_source':
                                                    translation.source
                                                }).data,
                    'initialOverrides':
                    SegmentOverrideSerializer(segment_overrides,
                                              many=True,
                                              context={
                                                  'translation_source':
                                                  translation.source
                                              }).data,
                },
                cls=DjangoJSONEncoder)
        })
Beispiel #35
0
def index(request, parent_page_id=None):
    if parent_page_id:
        parent_page = get_object_or_404(Page, id=parent_page_id)
    else:
        parent_page = Page.get_first_root_node()

    # This will always succeed because of the @user_passes_test above.
    root_page = get_explorable_root_page(request.user)

    # If this page isn't a descendant of the user's explorable root page,
    # then redirect to that explorable root page instead.
    if not (parent_page.pk == root_page.pk
            or parent_page.is_descendant_of(root_page)):
        return redirect('wagtailadmin_explore', root_page.pk)

    parent_page = parent_page.specific

    user_perms = UserPagePermissionsProxy(request.user)
    pages = (parent_page.get_children().prefetch_related(
        "content_type", "sites_rooted_here")
             & user_perms.explorable_pages())

    # Get page ordering
    ordering = request.GET.get('ordering', '-latest_revision_created_at')
    if ordering not in [
            'title', '-title', 'content_type', '-content_type', 'live',
            '-live', 'latest_revision_created_at',
            '-latest_revision_created_at', 'ord'
    ]:
        ordering = '-latest_revision_created_at'

    if ordering == 'ord':
        # preserve the native ordering from get_children()
        pass
    elif ordering == 'latest_revision_created_at':
        # order by oldest revision first.
        # Special case NULL entries - these should go at the top of the list.
        # Do this by annotating with Count('latest_revision_created_at'),
        # which returns 0 for these
        pages = pages.annotate(
            null_position=Count('latest_revision_created_at')).order_by(
                'null_position', 'latest_revision_created_at')
    elif ordering == '-latest_revision_created_at':
        # order by oldest revision first.
        # Special case NULL entries - these should go at the end of the list.
        pages = pages.annotate(
            null_position=Count('latest_revision_created_at')).order_by(
                '-null_position', '-latest_revision_created_at')
    else:
        pages = pages.order_by(ordering)

    # Don't paginate if sorting by page order - all pages must be shown to
    # allow drag-and-drop reordering
    do_paginate = ordering != 'ord'

    # We want specific page instances, but do not need streamfield values here
    pages = pages.defer_streamfields().specific()

    # allow hooks defer_streamfieldsyset
    for hook in hooks.get_hooks('construct_explorer_page_queryset'):
        pages = hook(parent_page, pages, request)

    # Pagination
    if do_paginate:
        paginator = Paginator(pages, per_page=50)
        pages = paginator.get_page(request.GET.get('p'))

    context = {
        'parent_page': parent_page.specific,
        'ordering': ordering,
        'pagination_query_params': "ordering=%s" % ordering,
        'pages': pages,
        'do_paginate': do_paginate,
        'locale': None,
        'translations': [],
    }

    if getattr(settings, 'WAGTAIL_I18N_ENABLED',
               False) and not parent_page.is_root():
        context.update({
            'locale':
            parent_page.locale,
            'translations': [{
                'locale':
                translation.locale,
                'url':
                reverse('wagtailadmin_explore', args=[translation.id]),
            } for translation in parent_page.get_translations().only(
                'id', 'locale').select_related('locale')],
        })

    return TemplateResponse(request, 'wagtailadmin/pages/index.html', context)
def index(request, parent_page_id=None):
    if parent_page_id:
        parent_page = get_object_or_404(Page, id=parent_page_id)
    else:
        parent_page = Page.get_first_root_node()

    # This will always succeed because of the @user_passes_test above.
    root_page = get_explorable_root_page(request.user)

    # If this page isn't a descendant of the user's explorable root page,
    # then redirect to that explorable root page instead.
    if not (
        parent_page.pk == root_page.pk or
        parent_page.is_descendant_of(root_page)
    ):
        return redirect('wagtailadmin_explore', root_page.pk)

    parent_page = parent_page.specific

    pages = parent_page.get_children().prefetch_related('content_type', 'sites_rooted_here')

    # Get page ordering
    ordering = request.GET.get('ordering', '-latest_revision_created_at')
    if ordering not in [
        'title',
        '-title',
        'content_type',
        '-content_type',
        'live', '-live',
        'latest_revision_created_at',
        '-latest_revision_created_at',
        'ord'
    ]:
        ordering = '-latest_revision_created_at'

    if ordering == 'ord':
        # preserve the native ordering from get_children()
        pass
    elif ordering == 'latest_revision_created_at':
        # order by oldest revision first.
        # Special case NULL entries - these should go at the top of the list.
        # Do this by annotating with Count('latest_revision_created_at'),
        # which returns 0 for these
        pages = pages.annotate(
            null_position=Count('latest_revision_created_at')
        ).order_by('null_position', 'latest_revision_created_at')
    elif ordering == '-latest_revision_created_at':
        # order by oldest revision first.
        # Special case NULL entries - these should go at the end of the list.
        pages = pages.annotate(
            null_position=Count('latest_revision_created_at')
        ).order_by('-null_position', '-latest_revision_created_at')
    else:
        pages = pages.order_by(ordering)

    # Don't paginate if sorting by page order - all pages must be shown to
    # allow drag-and-drop reordering
    do_paginate = ordering != 'ord'

    if do_paginate or pages.count() < 100:
        # Retrieve pages in their most specific form, so that custom
        # get_admin_display_title and get_url_parts methods on subclasses are respected.
        # However, skip this on unpaginated listings with >100 child pages as this could
        # be a significant performance hit. (This should only happen on the reorder view,
        # and hopefully no-one is having to do manual reordering on listings that large...)
        pages = pages.specific(defer=True)

    # allow hooks to modify the queryset
    for hook in hooks.get_hooks('construct_explorer_page_queryset'):
        pages = hook(parent_page, pages, request)

    # Pagination
    if do_paginate:
        paginator, pages = paginate(request, pages, per_page=50)

    return render(request, 'wagtailadmin/pages/index.html', {
        'parent_page': parent_page.specific,
        'ordering': ordering,
        'pagination_query_params': "ordering=%s" % ordering,
        'pages': pages,
        'do_paginate': do_paginate,
    })
def edit_translation(request, translation, instance):
    if isinstance(instance, Page):
        # Page
        # Note: Edit permission is already checked by the edit page view

        page_perms = instance.permissions_for_user(request.user)

        is_live = instance.live
        is_locked = instance.locked

        if instance.live_revision:
            last_published_at = instance.live_revision.created_at
            last_published_by = instance.live_revision.user
        else:
            last_published_at = instance.last_published_at
            last_published_by = None

        if instance.live:
            live_url = instance.full_url
        else:
            live_url = None

        can_publish = page_perms.can_publish()
        can_unpublish = page_perms.can_unpublish()
        can_lock = page_perms.can_lock()
        can_unlock = page_perms.can_unlock()
        can_delete = page_perms.can_delete()

    else:
        # Snippet
        # Note: Edit permission is already checked by the edit snippet view

        is_live = True
        is_locked = False
        last_published_at = None
        last_published_by = None
        live_url = None

        can_publish = True
        can_unpublish = False
        can_lock = False
        can_unlock = False
        can_delete = request.user.has_perm(
            get_permission_name("delete", instance.__class__)
        )

    source_instance = translation.source.get_source_instance()

    if request.method == "POST":
        if request.POST.get("action") == "publish":
            if isinstance(instance, Page):
                if not page_perms.can_publish():
                    raise PermissionDenied

            try:
                translation.save_target(user=request.user, publish=True)

            except ValidationError:
                messages.error(
                    request,
                    _(
                        "New validation errors were found when publishing '{object}' in {locale}. Please fix them or click publish again to ignore these translations for now."
                    ).format(
                        object=str(instance),
                        locale=translation.target_locale.get_display_name(),
                    ),
                )

            else:
                # Refresh instance to title in success message is up to date
                instance.refresh_from_db()

                string_segments = translation.source.stringsegment_set.all().order_by(
                    "order"
                )
                string_translations = string_segments.get_translations(
                    translation.target_locale
                )

                # Using annotate_translation as this ignores errors by default (so both errors and missing segments treated the same)
                if (
                    string_segments.annotate_translation(translation.target_locale)
                    .filter(translation__isnull=True)
                    .exists()
                ):
                    # One or more strings had an error
                    messages.warning(
                        request,
                        _(
                            "Published '{object}' in {locale} with missing translations - see below."
                        ).format(
                            object=str(instance),
                            locale=translation.target_locale.get_display_name(),
                        ),
                    )

                else:
                    messages.success(
                        request,
                        _("Published '{object}' in {locale}.").format(
                            object=str(instance),
                            locale=translation.target_locale.get_display_name(),
                        ),
                    )

        return redirect(request.path)

    string_segments = translation.source.stringsegment_set.all().order_by("order")
    string_translations = string_segments.get_translations(translation.target_locale)

    overridable_segments = translation.source.overridablesegment_set.all().order_by(
        "order"
    )
    segment_overrides = overridable_segments.get_overrides(translation.target_locale)
    related_object_segments = (
        translation.source.relatedobjectsegment_set.all().order_by("order")
    )

    tab_helper = TabHelper(source_instance)

    breadcrumb = []
    title_segment_id = None
    if isinstance(instance, Page):
        # find the closest common ancestor of the pages that this user has direct explore permission
        # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb
        cca = get_explorable_root_page(request.user)
        if cca:
            breadcrumb = [
                {
                    "id": page.id,
                    "isRoot": page.is_root(),
                    "title": page.title,
                    "exploreUrl": reverse("wagtailadmin_explore_root")
                    if page.is_root()
                    else reverse("wagtailadmin_explore", args=[page.id]),
                }
                for page in instance.get_ancestors(inclusive=False).descendant_of(
                    cca, inclusive=True
                )
            ]

        # Set to the ID of a string segment that represents the title.
        # If this segment has a translation, the title will be replaced with that translation.
        try:
            title_segment_id = string_segments.get(context__path="title").id
        except StringSegment.DoesNotExist:
            pass

    machine_translator = None
    translator = get_machine_translator()
    if translator and translator.can_translate(
        translation.source.locale, translation.target_locale
    ):
        machine_translator = {
            "name": translator.display_name,
            "url": reverse("wagtail_localize:machine_translate", args=[translation.id]),
        }

    segments = []

    for segment in string_segments:
        try:
            location_info = get_segment_location_info(
                source_instance,
                tab_helper,
                segment.context.path,
                segment.context.get_field_path(source_instance),
            )
        except FieldHasNoEditPanelError:
            continue

        segments.append(
            {
                "type": "string",
                "id": segment.id,
                "contentPath": segment.context.path,
                "source": segment.string.data,
                "location": location_info,
                "editUrl": reverse(
                    "wagtail_localize:edit_string_translation",
                    kwargs={
                        "translation_id": translation.id,
                        "string_segment_id": segment.id,
                    },
                ),
                "order": segment.order,
            }
        )

    for segment in overridable_segments:
        try:
            location_info = get_segment_location_info(
                source_instance,
                tab_helper,
                segment.context.path,
                segment.context.get_field_path(source_instance),
                widget=True,
            )
        except FieldHasNoEditPanelError:
            continue

        segments.append(
            {
                "type": "synchronised_value",
                "id": segment.id,
                "contentPath": segment.context.path,
                "location": location_info,
                "value": segment.data,
                "editUrl": reverse(
                    "wagtail_localize:edit_override",
                    kwargs={
                        "translation_id": translation.id,
                        "overridable_segment_id": segment.id,
                    },
                ),
                "order": segment.order,
            }
        )

    def get_source_object_info(segment):
        instance = segment.get_source_instance()

        if isinstance(instance, Page):
            return {
                "title": str(instance),
                "isLive": instance.live,
                "liveUrl": instance.full_url,
                "editUrl": reverse("wagtailadmin_pages:edit", args=[instance.id]),
                "createTranslationRequestUrl": reverse(
                    "wagtail_localize:submit_page_translation", args=[instance.id]
                ),
            }

        else:
            return {
                "title": str(instance),
                "isLive": True,
                "editUrl": reverse(
                    "wagtailsnippets:edit",
                    args=[
                        instance._meta.app_label,
                        instance._meta.model_name,
                        quote(instance.id),
                    ],
                ),
                "createTranslationRequestUrl": reverse(
                    "wagtail_localize:submit_snippet_translation",
                    args=[
                        instance._meta.app_label,
                        instance._meta.model_name,
                        quote(instance.id),
                    ],
                ),
            }

    def get_dest_object_info(segment):
        instance = segment.object.get_instance_or_none(translation.target_locale)
        if not instance:
            return

        if isinstance(instance, Page):
            return {
                "title": str(instance),
                "isLive": instance.live,
                "liveUrl": instance.full_url,
                "editUrl": reverse("wagtailadmin_pages:edit", args=[instance.id]),
            }

        else:
            return {
                "title": str(instance),
                "isLive": True,
                "editUrl": reverse(
                    "wagtailsnippets:edit",
                    args=[
                        instance._meta.app_label,
                        instance._meta.model_name,
                        quote(instance.id),
                    ],
                ),
            }

    def get_translation_progress(segment, locale):
        try:
            translation = Translation.objects.get(
                source__object_id=segment.object_id, target_locale=locale, enabled=True
            )

        except Translation.DoesNotExist:
            return None

        total_segments, translated_segments = translation.get_progress()

        return {
            "totalSegments": total_segments,
            "translatedSegments": translated_segments,
        }

    for segment in related_object_segments:
        try:
            location_info = get_segment_location_info(
                source_instance,
                tab_helper,
                segment.context.path,
                segment.context.get_field_path(source_instance),
            )
        except FieldHasNoEditPanelError:
            continue

        segments.append(
            {
                "type": "related_object",
                "id": segment.id,
                "contentPath": segment.context.path,
                "location": location_info,
                "order": segment.order,
                "source": get_source_object_info(segment),
                "dest": get_dest_object_info(segment),
                "translationProgress": get_translation_progress(
                    segment, translation.target_locale
                ),
            }
        )

    # Order segments by how they appear in the content panels
    # segment['location']['order'] is the content panel ordering
    # segment['order'] is the model field ordering
    # User's expect segments to follow the panel ordering as that's the ordering
    # that is used in the page editor of the source page. However, segments that
    # come from the same streamfield/inline panel are given the same value for
    # panel ordering, so we need to order by model field ordering as well (all
    # segments have a unique value for model field ordering)
    segments.sort(key=lambda segment: (segment["location"]["order"], segment["order"]))

    # Display a warning to the user if the schema of the source model has been updated since the source was last updated
    if translation.source.schema_out_of_date():
        messages.warning(
            request,
            _(
                "The data model for '{model_name}' has been changed since the last translation sync. "
                "If any new fields have been added recently, these may not be visible until the next translation sync."
            ).format(model_name=capfirst(source_instance._meta.verbose_name)),
        )

    return render(
        request,
        "wagtail_localize/admin/edit_translation.html",
        {
            "translation": translation,
            # These props are passed directly to the TranslationEditor react component
            "props": json.dumps(
                {
                    "adminBaseUrl": reverse("wagtailadmin_home"),
                    "object": {
                        "title": str(instance),
                        "titleSegmentId": title_segment_id,
                        "isLive": is_live,
                        "isLocked": is_locked,
                        "lastPublishedDate": last_published_at.strftime(DATE_FORMAT)
                        if last_published_at is not None
                        else None,
                        "lastPublishedBy": UserSerializer(last_published_by).data
                        if last_published_by is not None
                        else None,
                        "liveUrl": live_url,
                    },
                    "breadcrumb": breadcrumb,
                    "tabs": tab_helper.tabs_with_slugs,
                    "sourceLocale": {
                        "code": translation.source.locale.language_code,
                        "displayName": translation.source.locale.get_display_name(),
                    },
                    "locale": {
                        "code": translation.target_locale.language_code,
                        "displayName": translation.target_locale.get_display_name(),
                    },
                    "translations": [
                        {
                            "title": str(translated_instance),
                            "locale": {
                                "code": translated_instance.locale.language_code,
                                "displayName": translated_instance.locale.get_display_name(),
                            },
                            "editUrl": reverse(
                                "wagtailadmin_pages:edit", args=[translated_instance.id]
                            )
                            if isinstance(translated_instance, Page)
                            else reverse(
                                "wagtailsnippets:edit",
                                args=[
                                    translated_instance._meta.app_label,
                                    translated_instance._meta.model_name,
                                    quote(translated_instance.id),
                                ],
                            ),
                        }
                        for translated_instance in instance.get_translations().select_related(
                            "locale"
                        )
                    ],
                    "perms": {
                        "canPublish": can_publish,
                        "canUnpublish": can_unpublish,
                        "canLock": can_lock,
                        "canUnlock": can_unlock,
                        "canDelete": can_delete,
                    },
                    "links": {
                        "downloadPofile": reverse(
                            "wagtail_localize:download_pofile", args=[translation.id]
                        ),
                        "uploadPofile": reverse(
                            "wagtail_localize:upload_pofile", args=[translation.id]
                        ),
                        "unpublishUrl": reverse(
                            "wagtailadmin_pages:unpublish", args=[instance.id]
                        )
                        if isinstance(instance, Page)
                        else None,
                        "lockUrl": reverse(
                            "wagtailadmin_pages:lock", args=[instance.id]
                        )
                        if isinstance(instance, Page)
                        else None,
                        "unlockUrl": reverse(
                            "wagtailadmin_pages:unlock", args=[instance.id]
                        )
                        if isinstance(instance, Page)
                        else None,
                        "deleteUrl": reverse(
                            "wagtailadmin_pages:delete", args=[instance.id]
                        )
                        if isinstance(instance, Page)
                        else reverse(
                            "wagtailsnippets:delete",
                            args=[
                                instance._meta.app_label,
                                instance._meta.model_name,
                                quote(instance.pk),
                            ],
                        ),
                        "stopTranslationUrl": reverse(
                            "wagtail_localize:stop_translation", args=[translation.id]
                        ),
                    },
                    "previewModes": [
                        {
                            "mode": mode,
                            "label": label,
                            "url": reverse(
                                "wagtail_localize:preview_translation",
                                args=[translation.id],
                            )
                            if mode == instance.default_preview_mode
                            else reverse(
                                "wagtail_localize:preview_translation",
                                args=[translation.id, mode],
                            ),
                        }
                        for mode, label in (
                            instance.preview_modes if isinstance(instance, Page) else []
                        )
                    ],
                    "machineTranslator": machine_translator,
                    "segments": segments,
                    # We serialize the translation data using Django REST Framework.
                    # This gives us a consistent representation with the APIs so we
                    # can dynamically update translations in the view.
                    "initialStringTranslations": StringTranslationSerializer(
                        string_translations,
                        many=True,
                        context={"translation_source": translation.source},
                    ).data,
                    "initialOverrides": SegmentOverrideSerializer(
                        segment_overrides,
                        many=True,
                        context={"translation_source": translation.source},
                    ).data,
                },
                cls=DjangoJSONEncoder,
            ),
        },
    )