Fork me on GitHub
Back to documentation
package Statocles::Page::List;
# ABSTRACT: A page presenting a list of other pages

use Statocles::Base 'Class';
with 'Statocles::Page';
use List::Util qw( max );
use Statocles::Template;
use Statocles::Page::ListItem;

=attr pages

The pages that should be shown in this list.

=cut

has _pages => (
    is => 'ro',
    isa => ArrayRef[ConsumerOf['Statocles::Page']],
    init_arg => 'pages',
);

sub pages {
    my ( $self ) = @_;

    my %rewrite;
    if ( $self->type eq 'application/rss+xml' || $self->type eq 'application/atom+xml' ) {
        %rewrite = ( rewrite_mode => 'full' );
    }

    my @pages;
    for my $page ( @{ $self->_pages } ) {
        # Always re-wrap the page, even if it's already wrapped,
        # to change the rewrite_mode
        push @pages, Statocles::Page::ListItem->new(
            %rewrite,
            page => $page->isa( 'Statocles::Page::ListItem' ) ? $page->page : $page,
        );
    }

    return \@pages;
}

=attr next

The path to the next page in the pagination series.

=attr prev

The path to the previous page in the pagination series.

=cut

has [qw( next prev )] => (
    is => 'rw',
    isa => Path,
    coerce => Path->coercion,
);

=attr date

Get the date of this list. By default, this is the latest date of the first
page in the list of pages.

=cut

has '+date' => (
    lazy => 1,
    default => sub {
        my ( $self ) = @_;
        $self->pages->[0]->date;
    },
);

=attr search_change_frequency

Override the default L<search_change_frequency|Statocles::Page/search_change_frequency>
to C<daily>, because these pages aggregate other pages.

=cut

has '+search_change_frequency' => (
    default => sub { 'daily' },
);

=attr search_priority

Override the default L<search_priority|Statocles::Page/search_priority> to reduce
the rank of list pages to C<0.3>.

It is more important for users to get to the full page than
to get to this list page, which may contain truncated content, and whose relevant
content may appear 3-4 items down the page.

=cut

has '+search_priority' => (
    default => sub { 0.3 },
);

=method paginate

    my @pages = Statocles::Page::List->paginate( %args );

Build a paginated list of L<Statocles::Page::List> objects.

Takes a list of key-value pairs with the following keys:

    path    - An sprintf format string to build the path, like '/page-%i.html'.
              Pages are indexed started at 1.
    index   - The special, unique path for the first page. Optional.
    pages   - The arrayref of Statocles::Page::Document objects to paginate.
    after   - The number of items per page. Defaults to 5.

Return a list of Statocles::Page::List objects in numerical order, the index
page first (if any).

=cut

sub paginate {
    my ( $class, %args ) = @_;

    # Unpack the args so we can pass the rest to new()
    my $after = delete $args{after} // 5;
    my $pages = delete $args{pages};
    my $path_format = delete $args{path};
    my $index = delete $args{index};

    my @sets;
    for my $i ( 0..$#{$pages} ) {
        push @{ $sets[ int( $i / $after ) ] }, $pages->[ $i ];
    }

    my @retval;
    for my $i ( 0..$#sets ) {
        my $path = $index && $i == 0 ? $index : sprintf( $path_format, $i + 1 );
        my $prev = $index && $i == 1 ? $index : sprintf( $path_format, $i );
        my $next = $i != $#sets ? sprintf( $path_format, $i + 2 ) : '';

        # Remove index.html from link URLs
        s{/index[.]html$}{/} for ( $prev, $next );

        push @retval, $class->new(
            path => $path,
            pages => $sets[$i],
            ( $next ? ( next => $next ) : () ),
            ( $i > 0 ? ( prev => $prev ) : () ),
            date => $pages->[0]->date,
            %args,
        );
    }

    return @retval;
}

=method vars

    my %vars = $page->vars;

Get the template variables for this page.

=cut

sub vars {
    my ( $self ) = @_;
    return (
        pages => $self->pages,
    );
}

1;
__END__

=head1 DESCRIPTION

A List page contains a set of other pages. These are frequently used for index
pages.