Ejemplo n.º 1
0
    def test_construct_queryset_hook(self):
        page = SimplePage(title="Test shown", content="hello")
        Page.get_first_root_node().add_child(instance=page)

        page_not_shown = SimplePage(title="Test not shown", content="hello")
        Page.get_first_root_node().add_child(instance=page_not_shown)

        def filter_pages(pages, request):
            return pages.filter(id=page.id)

        with self.register_hook('construct_page_chooser_queryset',
                                filter_pages):
            response = self.get()
        self.assertEqual(len(response.context['pages']), 1)
        self.assertEqual(response.context['pages'][0].specific, page)
Ejemplo n.º 2
0
def move_choose_destination(request, page_to_move_id, viewed_page_id=None):
    page_to_move = get_object_or_404(Page, id=page_to_move_id)
    page_perms = page_to_move.permissions_for_user(request.user)
    if not page_perms.can_move():
        raise PermissionDenied

    if viewed_page_id:
        viewed_page = get_object_or_404(Page, id=viewed_page_id)
    else:
        viewed_page = Page.get_first_root_node()

    viewed_page.can_choose = page_perms.can_move_to(viewed_page)

    child_pages = []
    for target in viewed_page.get_children():
        # can't move the page into itself or its descendants
        target.can_choose = page_perms.can_move_to(target)

        target.can_descend = (
            not (target == page_to_move or target.is_child_of(page_to_move))
            and target.get_children_count())

        child_pages.append(target)

    # Pagination
    paginator, child_pages = paginate(request, child_pages, per_page=50)

    return render(
        request, 'tuiuiuadmin/pages/move_choose_destination.html', {
            'page_to_move': page_to_move,
            'viewed_page': viewed_page,
            'child_pages': child_pages,
        })
Ejemplo n.º 3
0
    def clean(self):

        cleaned_data = super(TuiuiuAdminPageForm, self).clean()
        if 'slug' in self.cleaned_data:
            if not Page._slug_is_available(cleaned_data['slug'],
                                           self.parent_page, self.instance):
                self.add_error(
                    'slug',
                    forms.ValidationError(_("This slug is already in use")))

        # Check scheduled publishing fields
        go_live_at = cleaned_data.get('go_live_at')
        expire_at = cleaned_data.get('expire_at')

        # Go live must be before expire
        if go_live_at and expire_at:
            if go_live_at > expire_at:
                msg = _('Go live date/time must be before expiry date/time')
                self.add_error('go_live_at', forms.ValidationError(msg))
                self.add_error('expire_at', forms.ValidationError(msg))

        # Expire at must be in the future
        if expire_at and expire_at < timezone.now():
            self.add_error(
                'expire_at',
                forms.ValidationError(
                    _('Expiry date/time must be in the future')))

        # Don't allow an existing first_published_at to be unset by clearing the field
        if 'first_published_at' in cleaned_data and not cleaned_data[
                'first_published_at']:
            del cleaned_data['first_published_at']

        return cleaned_data
Ejemplo n.º 4
0
    def setUp(self):
        self.site_2_page = SimplePage(
            title="Site 2 page",
            slug="site_2_page",
            content="Hello",
        )
        Page.get_first_root_node().add_child(instance=self.site_2_page)
        self.site_2_subpage = SimplePage(
            title="Site 2 subpage",
            slug="site_2_subpage",
            content="Hello again",
        )
        self.site_2_page.add_child(instance=self.site_2_subpage)

        self.site_2 = Site.objects.create(
            hostname='example.com',
            port=8080,
            root_page=Page.objects.get(pk=self.site_2_page.pk),
            is_default_site=False
        )
        self.about_us_page = SimplePage.objects.get(url_path='/home/about-us/')
Ejemplo n.º 5
0
    def setUp(self):
        root = Page.objects.first()
        other_home = Page(title='Other Root')
        root.add_child(instance=other_home)

        self.default_site = Site.objects.get(is_default_site=True)
        self.other_site = Site.objects.create(hostname='other',
                                              root_page=other_home)

        self.test_setting = TestSetting.objects.create(
            title='Site title',
            email='*****@*****.**',
            site=self.default_site)

        self.other_setting = TestSetting.objects.create(
            title='Other title',
            email='*****@*****.**',
            site=self.other_site)
Ejemplo n.º 6
0
    def get_page(self):
        (content_type_app_name, content_type_model_name,
         parent_page_id) = self.args
        try:
            content_type = ContentType.objects.get_by_natural_key(
                content_type_app_name, content_type_model_name)
        except ContentType.DoesNotExist:
            raise Http404

        page = content_type.model_class()()
        parent_page = get_object_or_404(Page, id=parent_page_id).specific
        # We need to populate treebeard's path / depth fields in order to
        # pass validation. We can't make these 100% consistent with the rest
        # of the tree without making actual database changes (such as
        # incrementing the parent's numchild field), but by calling treebeard's
        # internal _get_path method, we can set a 'realistic' value that will
        # hopefully enable tree traversal operations
        # to at least partially work.
        page.depth = parent_page.depth + 1
        # Puts the page at the maximum possible path
        # for a child of `parent_page`.
        page.path = Page._get_children_path_interval(parent_page.path)[1]
        return page
Ejemplo n.º 7
0
def index(request, parent_page_id=None):
    if parent_page_id:
        parent_page = get_object_or_404(Page, id=parent_page_id).specific
    else:
        parent_page = Page.get_first_root_node().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, 'tuiuiuadmin/pages/index.html', {
            'parent_page': parent_page.specific,
            'ordering': ordering,
            'pagination_query_params': "ordering=%s" % ordering,
            'pages': pages,
            'do_paginate': do_paginate,
        })
Ejemplo n.º 8
0
 def handle(self, *args, **options):
     for node in Page.get_root_nodes():
         self.set_subtree(node)
Ejemplo n.º 9
0
 def test_empty_queryset(self):
     self.assertEqual(
         Page.get_first_root_node(),
         Page.objects.none().first_common_ancestor())
Ejemplo n.º 10
0
 def test_all_pages_include_self_strict(self):
     self.assertEqual(
         Page.get_first_root_node(),
         Page.objects.first_common_ancestor(include_self=True, strict=True))
Ejemplo n.º 11
0
 def test_all_pages(self):
     self.assertEqual(
         Page.get_first_root_node(),
         Page.objects.first_common_ancestor())
Ejemplo n.º 12
0
def browse(request, parent_page_id=None):
    # A missing or empty page_type parameter indicates 'all page types'
    # (i.e. descendants of tuiuiucore.page)
    page_type_string = request.GET.get('page_type') or 'tuiuiucore.page'
    user_perm = request.GET.get('user_perms', False)

    try:
        desired_classes = page_models_from_string(page_type_string)
    except (ValueError, LookupError):
        raise Http404

    # Find parent page
    if parent_page_id:
        parent_page = get_object_or_404(Page, id=parent_page_id)
    elif desired_classes == (Page, ):
        # Just use the root page
        parent_page = Page.get_first_root_node()
    else:
        # Find the highest common ancestor for the specific classes passed in
        # In many cases, such as selecting an EventPage under an EventIndex,
        # this will help the administrator find their page quicker.
        all_desired_pages = filter_page_type(Page.objects.all(),
                                             desired_classes)
        parent_page = all_desired_pages.first_common_ancestor()

    # Get children of parent page
    pages = parent_page.get_children().specific()

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

    # Filter them by page type
    if desired_classes != (Page, ):
        # restrict the page listing to just those pages that:
        # - are of the given content type (taking into account class inheritance)
        # - or can be navigated into (i.e. have children)
        choosable_pages = filter_page_type(pages, desired_classes)
        descendable_pages = pages.filter(numchild__gt=0)
        pages = choosable_pages | descendable_pages

    can_choose_root = request.GET.get('can_choose_root', False)

    # Do permission lookups for this user now, instead of for every page.
    permission_proxy = UserPagePermissionsProxy(request.user)

    # Parent page can be chosen if it is a instance of desired_classes
    parent_page.can_choose = can_choose_page(parent_page, permission_proxy,
                                             desired_classes, can_choose_root,
                                             user_perm)

    # Pagination
    # We apply pagination first so we don't need to walk the entire list
    # in the block below
    paginator, pages = paginate(request, pages, per_page=25)

    # Annotate each page with can_choose/can_decend flags
    for page in pages:
        page.can_choose = can_choose_page(page, permission_proxy,
                                          desired_classes, can_choose_root,
                                          user_perm)
        page.can_descend = page.get_children_count()

    # Render
    return render_modal_workflow(
        request, 'tuiuiuadmin/chooser/browse.html',
        'tuiuiuadmin/chooser/browse.js',
        shared_context(
            request, {
                'parent_page':
                parent_page,
                'parent_page_id':
                parent_page.pk,
                'pages':
                pages,
                'search_form':
                SearchForm(),
                'page_type_string':
                page_type_string,
                'page_type_names': [
                    desired_class.get_verbose_name()
                    for desired_class in desired_classes
                ],
                'page_types_restricted':
                (page_type_string != 'tuiuiucore.page')
            }))
Ejemplo n.º 13
0
    def handle(self, **options):
        any_problems_fixed = False

        for page in Page.objects.all():
            try:
                page.specific
            except page.specific_class.DoesNotExist:
                self.stdout.write(
                    "Page %d (%s) is missing a subclass record; deleting." %
                    (page.id, page.title))
                any_problems_fixed = True
                page.delete()

        (bad_alpha, bad_path, orphans, bad_depth,
         bad_numchild) = Page.find_problems()

        if bad_depth:
            self.stdout.write("Incorrect depth value found for pages: %s" %
                              self.numberlist_to_string(bad_depth))
        if bad_numchild:
            self.stdout.write("Incorrect numchild value found for pages: %s" %
                              self.numberlist_to_string(bad_numchild))

        if bad_depth or bad_numchild:
            Page.fix_tree(destructive=False)
            any_problems_fixed = True

        if orphans:
            # The 'orphans' list as returned by treebeard only includes pages that are
            # missing an immediate parent; descendants of orphans are not included.
            # Deleting only the *actual* orphans is a bit silly (since it'll just create
            # more orphans), so generate a queryset that contains descendants as well.
            orphan_paths = Page.objects.filter(id__in=orphans).values_list(
                'path', flat=True)
            filter_conditions = []
            for path in orphan_paths:
                filter_conditions.append(Q(path__startswith=path))

            # combine filter_conditions into a single ORed condition
            final_filter = functools.reduce(operator.or_, filter_conditions)

            # build a queryset of all pages to be removed; this must be a vanilla Django
            # queryset rather than a treebeard MP_NodeQuerySet, so that we bypass treebeard's
            # custom delete() logic that would trip up on the very same corruption that we're
            # trying to fix here.
            pages_to_delete = models.query.QuerySet(Page).filter(final_filter)

            self.stdout.write("Orphaned pages found:")
            for page in pages_to_delete:
                self.stdout.write("ID %d: %s" % (page.id, page.title))
            self.stdout.write('')

            if options.get('interactive', True):
                yes_or_no = six.moves.input("Delete these pages? [y/N] ")
                delete_orphans = yes_or_no.lower().startswith('y')
                self.stdout.write('')
            else:
                # Running tests, check for the "delete_orphans" option
                delete_orphans = options.get('delete_orphans', False)

            if delete_orphans:
                deletion_count = len(pages_to_delete)
                pages_to_delete.delete()
                self.stdout.write(
                    "%d orphaned page%s deleted." %
                    (deletion_count, "s" if deletion_count != 1 else ""))
                any_problems_fixed = True

        if any_problems_fixed:
            # re-run find_problems to see if any new ones have surfaced
            (bad_alpha, bad_path, orphans, bad_depth,
             bad_numchild) = Page.find_problems()

        if any((bad_alpha, bad_path, orphans, bad_depth, bad_numchild)):
            self.stdout.write("Remaining problems (cannot fix automatically):")
            if bad_alpha:
                self.stdout.write(
                    "Invalid characters found in path for pages: %s" %
                    self.numberlist_to_string(bad_alpha))
            if bad_path:
                self.stdout.write("Invalid path length found for pages: %s" %
                                  self.numberlist_to_string(bad_path))
            if orphans:
                self.stdout.write("Orphaned pages found: %s" %
                                  self.numberlist_to_string(orphans))
            if bad_depth:
                self.stdout.write("Incorrect depth value found for pages: %s" %
                                  self.numberlist_to_string(bad_depth))
            if bad_numchild:
                self.stdout.write(
                    "Incorrect numchild value found for pages: %s" %
                    self.numberlist_to_string(bad_numchild))

        elif any_problems_fixed:
            self.stdout.write("All problems fixed.")
        else:
            self.stdout.write("No problems found.")
Ejemplo n.º 14
0
 def get_root_page(self, request):
     return Page.get_first_root_node()