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 )