# Opera-specific bookmarks parser and producer package Bookmarks::Opera; use strict; use warnings; use base 'Bookmarks::Parser'; # Last updated, September 2011, # Opera Hotlist version 21 my @op_bookmark_fields = ( "ACTIVE", "CREATED", "DELETABLE", "DESCRIPTION", "DISPLAY URL", "ICONFILE", "ID", "IN PANEL", "MOVE_IS_COPY", "NAME", "ON PERSONALBAR", "PANEL_POS", "PARTNERID", "PERSONALBAR_POS", "SEPARATOR_ALLOWED", "SHORT NAME", "TARGET", "TRASH FOLDER", "UNIQUEID", "URL", "VISITED", ); my %op_bookmark_fields = map { # No qr{}x, field names contain significant whitespace $_ => qr{^\s+$_=(.*)} } @op_bookmark_fields; sub _parse_file { my ($self, $filename) = @_; return undef if(!-e $filename); my $fh; my $curitem = {}; my $curfolder = {}; open $fh, "<$filename" or die "Can't open $filename ($!)"; while (my $line = <$fh>) { # chomp $line; $line =~ s/[\r\n]//g; next if($line =~ /^Opera Hotlist version/); next if($line =~ /^Options:/); if ($line =~ m{^ \s* $}x) { if($curitem->{start}) { delete $curitem->{start}; $curitem->{parent} = $curfolder->{id}; $self->add_bookmark($curitem, $curfolder->{id}); if($curitem->{type} eq 'folder') { $curfolder = $curitem; } $curitem = {}; } } if($line eq '-') { $curfolder = $self->{_items}{$curfolder->{parent}}; # ($curfolder) = grep { $_->{id} eq $curfolder->{parent} } # @{$self->{_items}}; } if($line =~/^#(FOLDER|URL)/) { $curitem->{start} = 1; $curitem->{type} = lc($1); } if($curitem->{start}) { for my $key (keys %op_bookmark_fields) { my $re = $op_bookmark_fields{$key}; if ($line =~ $re) { my $value = $1; my $nicename = lc $key; $nicename =~ s{\s}{_}g; $nicename =~ s{iconfile}{icon}; $curitem->{$nicename} = $value; } } } } # Deal with last element if there's no closing empty line if ($curitem->{start}) { delete $curitem->{start}; $curitem->{parent} = $curfolder->{id}; $self->add_bookmark($curitem, $curfolder->{id}); $curfolder = $curitem if $curitem->{type} eq 'folder'; $curitem = {}; } close($fh); return $self; } sub get_header_as_string { my ($self) = @_; my $header = << "HEADER"; Opera Hotlist version 2.0 Options: encoding = utf8, version=21 HEADER return $header; } { my $folorder = 0; sub get_item_as_string { my ($self, $item) = @_; if(!defined $item->{id} || !$self->{_items}{$item->{id}}) { warn "No such item in get_item_as_string"; return; } my $string = ''; my ($id, $url, $name, $visited, $created, $modified, $icon, $desc, $expand, $trash, $order) = ($item->{id} || 0, $item->{url} || '', $item->{name} || '', $item->{visited} || 0, $item->{created} || time(), $item->{modified} || 0, $item->{icon} || '', $item->{description} || '', $item->{expanded} || '', $item->{trash} || '', $item->{order} || undef); if($item->{type} eq 'folder') { if(!defined($order)) { $folorder = 0; } $string .= "#FOLDER\n"; $string .= " ID=$id\n"; $string .= " NAME=$name\n"; $string .= " CREATED=$created\n"; $string .= " TRASH FOLDER=$trash\n" if($trash); $string .= " VISITED=$visited\n" if($visited); $string .= " EXPANDED=$expand\n" if($expand); $string .= " DESCRIPTION=$desc\n" if($desc); $string .= " ICONFILE=$icon\n" if($icon); $string .= " ORDER=$order\n" if(defined $order); $string .= "\n"; $string .= $self->get_item_as_string($self->{_items}{$_}) foreach (@{$item->{children}}); $string .= "-\n"; } elsif($item->{type} eq 'url') { if(!defined($order)) { $order = $folorder++; } $string .= "#URL\n"; $string .= " ID=$id\n"; $string .= " NAME=$name\n"; $string .= " URL=$url\n" if($url); $string .= " CREATED=$created\n"; $string .= " TRASH FOLDER=$trash\n" if($trash); $string .= " VISITED=$visited\n" if($visited); $string .= " EXPANDED=$expand\n" if($expand); $string .= " DESCRIPTION=$desc\n" if($desc); $string .= " ICONFILE=$icon\n" if($icon); $string .= " ORDER=$order\n" if(defined $order); $string .= "\n"; } return $string; } } 1; __END__ =head1 NAME Bookmarks::Opera - Opera style bookmarks. =head1 SYNOPSIS use Data::Dumper; use Bookmarks::Parser; # You don't need to explicitly use Bookmarks::Opera my $parser = Bookmarks::Parser->new(); # Existing Opera bookmark file my $file = "bookmarks.adr"; my $bookmarks = $parser->parse({filename => $file}); my @nodes = $bookmarks->get_top_level(); my @tree; # Depth-first bookmarks tree visit while (@nodes) { my $node = shift @nodes; push @tree, $node; if ($node->{children}) { push @nodes, $bookmarks->get_from_id($_) for @{ $node->{children} }; } } print Dumper(\@tree); =head1 DESCRIPTION A subclass of L for handling Opera bookmarks. =head1 METHODS =head2 C =head2 C =head2 C See L for these methods. =head1 AUTHOR Jess Robinson Cosimo Streppone =head1 LICENSE This library is free software, you can redistribute it and/or modify it under the same terms as Perl itself. =cut