RecentInfo-Manager

 view release on metacpan or  search on metacpan

blog/blog.markdown  view on Meta::CPAN

:-( I'm looking to finally add "recently used files" support to my command line programs, to make attaching files to mails more convenient, but the XDG spec doesn't have a valid DTD

[XDG Desktop Bookmark Spec](https://www.freedesktop.org/wiki/Specifications/desktop-bookmark-spec/) - also used for the recently used files, but the DTD is not valid for `recently-used.xbel`

[XBEL homepage](https://pyxml.sourceforge.net/topics/xbel/) - some kind of XBEL, but not applicable for ingesting existing files

> \[Corion]: (and of course, manually editing the file results in either no change, or an empty recently-used list, without error messages to validate. And all things I find online are parsers, not writers)
> \[Corion]: I guess I have to dive into the ad-hoc generators and parsers, and generate a DTD from that :-(

[Some shell script that does the reverse](https://github.com/laodzu/gnome-recent)

Via Stackoverflow [some Python library](https://github.com/xenomachina/recently_used)
that points to [`Gtk.RecentManager`](https://docs.gtk.org/gtk3/class.RecentManager.html).

lib/RecentInfo/Entry.pm  view on Meta::CPAN

);

sub to_native( $self ) {
    my $href = $self->href;
    return $href =~ m!^file:!
        ? URI->new( $href )->file
        : $href
}

state $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs( bookmark => "http://www.freedesktop.org/standards/desktop-bookmarks");
$xpc->registerNs( mime     => "http://www.freedesktop.org/standards/shared-mime-info" );

sub as_XML_fragment($self, $doc) {
    my $bookmark = $doc->createElement('bookmark');
    $bookmark->setAttribute( 'href' => $self->href );
    # Validate that $modified, $visited etc. are proper DateTime strings
    # We enforce here a Z timezone

    for my $attr (qw(added modified visited )) {
        my $at = $self->$attr;

lib/RecentInfo/Entry.pm  view on Meta::CPAN

        };

        $bookmark->setAttribute( $attr => $self->$attr );
    };
    my $info = $bookmark->addNewChild( undef, 'info' );
    my $metadata = $info->addNewChild( undef, 'metadata' );
    #my $mime = $metadata->addNewChild( 'mime', 'mime-type' );
    my $mime = $metadata->addNewChild( undef,'mime:mime-type' );
    $mime->setAttribute( type => $self->mime_type );
    #$mime->appendText( $self->mime_type );
    $metadata->setAttribute('owner' => 'http://freedesktop.org' );
    # Should we allow this to be empty, or should we leave it out completely then?!

    if ($self->othermeta->@* ) {
        my $parser = XML::LibXML->new();
        for my $other ($self->othermeta->@* ) {
            $info->addChild( $parser->parse_balanced_chunk( $other, 'UTF-8' )->firstChild);
        }
    };

    if( $self->groups->@* ) {

lib/RecentInfo/Entry.pm  view on Meta::CPAN


    my $applications = $metadata->addNewChild( undef, "bookmark:applications" );
    for my $application ($self->applications->@* ) {
        $applications->addChild( $application->as_XML_fragment( $doc ));
    };

    return $bookmark;
}

sub from_XML_fragment( $class, $frag ) {
    my $meta = $xpc->findnodes('./info[1]/metadata[@owner="http://freedesktop.org"]', $frag)->[0];
    if(! $meta) {
        warn $frag->toString;
        croak "Invalid xml?! No <info>/<metadata> element found"
    };

    my $othermeta = $xpc->findnodes('./info[1]/metadata[@owner!="http://freedesktop.org"]', $frag);
    my @othermeta = map { $_->toString } $othermeta->@*;

    my %meta = (
        mime_type => $xpc->find('./mime:mime-type/@type', $meta)->[0]->nodeValue,
    );

    my @applications = $xpc->find('./bookmark:applications/bookmark:application', $meta)->@*;
    if( !@applications ) {
        warn $meta->toString;
        die "No applications found";

lib/RecentInfo/Manager/XBEL.pm  view on Meta::CPAN

    $self->entries->@* = sort { $a->visited cmp $b->visited } $self->entries->@*;

    return $res
}

sub toString( $self ) {
    my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
    my $xbel = $doc->createElement('xbel');
    $doc->setDocumentElement($xbel);
    $xbel->setAttribute("version" => '1.0');
    $xbel->setAttribute("xmlns:bookmark" => "http://www.freedesktop.org/standards/desktop-bookmarks");
    $xbel->setAttribute("xmlns:mime" => "http://www.freedesktop.org/standards/shared-mime-info");
    for my $bm ($self->entries->@*) {
        $xbel->addChild($bm->as_XML_fragment( $doc ));
    };

    my $pp = XML::LibXML::PrettyPrint->new(
        indent_string => '  ',
        element => {
            compact => [qw[ bookmark:group ]],
        },
    );

t/01-roundtrip.t  view on Meta::CPAN

    $bm = $reconstructed->fromString( $xml );
    $reconstructed->entries->@* = $bm->@*;

    is $xbel, $reconstructed, "The reconstructed data structure is identical to the first parse";
}

done_testing();

__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<xbel xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks"
      xmlns:mime="http://www.freedesktop.org/standards/shared-mime-info"
      version="1.0">
  <bookmark href="file:///home/corion/Projekte/MIME-Detect/Changes" added="2024-06-06T15:59:35.484580Z" modified="2024-06-06T15:59:35.484583Z" visited="2024-06-06T15:59:35.484580Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="text/plain"/>
        <bookmark:groups>
          <bookmark:group>geany</bookmark:group>
        </bookmark:groups>
        <bookmark:applications>
          <bookmark:application name="geany" exec="&apos;geany %u&apos;" modified="2024-06-06T15:59:35.484582Z" count="1"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
</xbel>
---
<?xml version="1.0" encoding="UTF-8"?>
<xbel xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks"
      xmlns:mime="http://www.freedesktop.org/standards/shared-mime-info"
      version="1.0"/>
---
<?xml version="1.0" encoding="UTF-8"?>
<xbel xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks"
      xmlns:mime="http://www.freedesktop.org/standards/shared-mime-info"
      version="1.0">
  <bookmark href="file:///home/corion/Projekte/MIME-Detect/Changes" added="2024-06-06T15:59:35.484580Z" modified="2024-06-06T15:59:35.484583Z" visited="2024-06-06T15:59:35.484580Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="text/plain"/>
        <bookmark:groups>
          <bookmark:group>geany</bookmark:group>
          <bookmark:group>Office</bookmark:group>
        </bookmark:groups>
        <bookmark:applications>
          <bookmark:application name="geany" exec="&apos;geany %u&apos;" modified="2024-06-06T15:59:35.484582Z" count="1"/>
          <bookmark:application name="geany2" exec="&apos;geany %u&apos;" modified="2024-06-06T15:59:35.484582Z" count="1"/>
        </bookmark:applications>
      </metadata>
    </info>
  </bookmark>
</xbel>

--- We (well, XSD) don't handle arbitrary metadata well
<?xml version="1.0" encoding="UTF-8"?>
<xbel xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks"
      xmlns:mime="http://www.freedesktop.org/standards/shared-mime-info"
      version="1.0">
  <bookmark href="file:///home/corion/Projekte/MIME-Detect/Changes" added="2024-06-06T15:59:35.484580Z" modified="2024-06-06T15:59:35.484583Z" visited="2024-06-06T15:59:35.484580Z">
    <info>
      <metadata owner="http://freedesktop.org">
        <mime:mime-type type="text/plain"/>
        <bookmark:groups>
          <bookmark:group>geany</bookmark:group>
          <bookmark:group>Office</bookmark:group>
        </bookmark:groups>
        <bookmark:applications>
          <bookmark:application name="geany" exec="&apos;geany %u&apos;" modified="2024-06-06T15:59:35.484582Z" count="1"/>
          <bookmark:application name="geany2" exec="&apos;geany %u&apos;" modified="2024-06-06T15:59:35.484582Z" count="1"/>
        </bookmark:applications>
      </metadata>

xsd/recently-used-xbel-internal.xsd  view on Meta::CPAN

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks"
    xmlns:mime="http://www.freedesktop.org/standards/shared-mime-info"
    targetNamespace="http://www.freedesktop.org/standards/desktop-bookmarks"
    >

<xs:import
    namespace="http://www.freedesktop.org/standards/shared-mime-info"
    schemaLocation="shared-mime-info.xsd"
    />

<xs:complexType name="groupType">
  <xs:simpleContent>
    <xs:extension base="xs:string" />
  </xs:simpleContent>
</xs:complexType>

<xs:complexType name="applicationType">

xsd/recently-used-xbel.xsd  view on Meta::CPAN

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:bookmark="http://www.freedesktop.org/standards/desktop-bookmarks"
    xmlns:mime="http://www.freedesktop.org/standards/shared-mime-info"
    xmlns=""
    xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
    vc:minVersion="1.1"
    >

<xs:import
    namespace="http://www.freedesktop.org/standards/shared-mime-info"
    schemaLocation="shared-mime-info.xsd"
    />

<xs:import
    namespace="http://www.freedesktop.org/standards/desktop-bookmarks"
    schemaLocation="recently-used-xbel-internal.xsd"
    />

<xs:complexType name="groupType">
  <xs:simpleContent>
    <xs:extension base="xs:string" />
  </xs:simpleContent>
</xs:complexType>

<xs:complexType name="applicationType">

xsd/recently-used-xbel.xsd  view on Meta::CPAN

  <xs:sequence>
      <xs:element ref="mime:mime-type" maxOccurs="1" minOccurs="1" />
      <xs:element name="icon" type="xs:string" maxOccurs="1" minOccurs="0" />
      <xs:element ref="bookmark:groups" maxOccurs="1" minOccurs="0" />
      <xs:element ref="bookmark:applications" maxOccurs="1" minOccurs="0" />
      <xs:element name="private" type="xs:boolean" maxOccurs="1" minOccurs="0" />
  </xs:sequence>
  <xs:attribute name="owner" use="required">
      <xs:simpleType>
          <xs:restriction base="xs:string">
              <xs:enumeration value="http://freedesktop.org"/>
          </xs:restriction>
      </xs:simpleType>
  </xs:attribute>
</xs:complexType>

<!--
<xs:complexType name="metadataTypeOther">
  <xs:sequence>
  <xs:any processContents="skip"/>
  </xs:sequence>

xsd/shared-mime-info.xsd  view on Meta::CPAN

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns="http://www.freedesktop.org/standards/shared-mime-info"
           targetNamespace="http://www.freedesktop.org/standards/shared-mime-info"
           >

<xs:element name="mime-type" />

</xs:schema>



( run in 0.645 second using v1.01-cache-2.11-cpan-299005ec8e3 )