Developer-Dashboard

 view release on metacpan or  search on metacpan

lib/Developer/Dashboard/PageStore.pm  view on Meta::CPAN

sub _existing_page_file {
    my ( $self, $id ) = @_;
    for my $file ( $self->_page_file_candidates($id) ) {
        return $file if -f $file;
    }
    return;
}

# _load_page_file($file, %args)
# Loads and parses one bookmark file from disk, with raw nav/*.tt fragment fallback.
# Input: bookmark file path string plus optional saved-page id.
# Output: Developer::Dashboard::PageDocument object.
sub _load_page_file {
    my ( $self, $file, %args ) = @_;
    my $instruction = $self->_read_saved_instruction($file);
    my $page = eval { Developer::Dashboard::PageDocument->from_instruction($instruction) };
    return $page if $page;

    my $id = $args{id} || '';
    if ( $id =~ m{\Anav/.+\.tt\z} && $self->_looks_like_raw_nav_fragment($instruction) ) {
        return $self->_raw_nav_fragment_page(
            id          => $id,
            instruction => $instruction,
        );
    }

    die( $@ || "Unable to load bookmark file $file" );
}

# _raw_nav_fragment_page(%args)
# Wraps a raw nav/*.tt Template Toolkit fragment file as a renderable page document.
# Input: saved nav id and raw instruction text string.
# Output: Developer::Dashboard::PageDocument object.
sub _raw_nav_fragment_page {
    my ( $self, %args ) = @_;
    my $id = $args{id} || die 'Missing raw nav fragment id';
    my $instruction = defined $args{instruction} ? $args{instruction} : '';
    return Developer::Dashboard::PageDocument->new(
        id     => $id,
        title  => basename($id),
        layout => { body => $instruction },
        meta   => { source_format => 'raw-nav-tt' },
    );
}

# _looks_like_raw_nav_fragment($instruction)
# Decides whether one nav/*.tt file is a real raw TT/HTML fragment instead of junk text.
# Input: raw saved file text string.
# Output: boolean true when the file looks like raw TT/HTML nav content.
sub _looks_like_raw_nav_fragment {
    my ( $self, $instruction ) = @_;
    return 0 if !defined $instruction || $instruction eq '';
    return 1 if $instruction =~ /\[%/;
    return 1 if $instruction =~ /<\s*[A-Za-z!\/][^>]*>/;
    return 0;
}

# _read_saved_instruction($file)
# Reads one saved bookmark file and normalizes older-invalid UTF-8 bytes.
# Input: bookmark file path string.
# Output: decoded instruction text string that is safe to emit as UTF-8 HTML/text.
sub _read_saved_instruction {
    my ( $self, $file ) = @_;
    open my $fh, '<:raw', $file or die "Unable to read $file: $!";
    local $/;
    my $raw = <$fh>;
    close $fh or die "Unable to close $file: $!";
    return '' if !defined $raw;
    my $text = eval { decode( 'UTF-8', $raw, FB_CROAK ) } || decode( 'UTF-8', $raw, FB_DEFAULT );
    return $self->_normalize_legacy_icon_markup($text);
}

# _normalize_legacy_icon_markup($text)
# Repairs browser-visible icon placeholders left behind by malformed older bookmark bytes.
# Input: decoded bookmark instruction text string.
# Output: normalized instruction text string with stable fallback glyphs in icon HTML contexts.
sub _normalize_legacy_icon_markup {
    my ( $self, $text ) = @_;
    return '' if !defined $text;
    $text =~ s/\x{1F9D1}\x{FFFD}\x{1F4BB}/\x{1F9D1}\x{200D}\x{1F4BB}/g;
    $text =~ s{(<h2>)\x{FFFD}(\s+)}{$1â—ˆ$2}g;
    $text =~ s{(<span\s+class="icon">)[^<]*\x{FFFD}[^<]*(</span>)}{$1🏷️$2}g;
    return $text;
}

# _saved_page_entries_for_root($root)
# Recursively lists bookmark file entries under one saved-page root.
# Input: bookmark root directory path string.
# Output: list of hash references with id and file keys.
sub _saved_page_entries_for_root {
    my ( $self, $root ) = @_;
    return if !defined $root || !-d $root;

    my @entries;
    File::Find::find(
        {
            no_chdir => 1,
            wanted   => sub {
                return if !-f $_;
                my $rel = File::Spec->abs2rel( $File::Find::name, $root );
                $rel =~ s{\\}{/}g;
                push @entries, {
                    id   => $rel,
                    file => $File::Find::name,
                };
            },
        },
        $root,
    );

    return @entries;
}

1;

__END__

=head1 NAME

Developer::Dashboard::PageStore - page persistence and token transport

=head1 SYNOPSIS

  my $store = Developer::Dashboard::PageStore->new(paths => $paths);
  my $page  = $store->load_saved_page('welcome');

=head1 DESCRIPTION

This module persists saved page instruction documents and handles transient
encoded page transport.

=head1 METHODS

=head2 new, page_file, save_page, load_saved_page, load_transient_page, encode_page, editable_url, render_url, source_url, list_saved_pages, migrate_legacy_json_pages



( run in 1.003 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )