From 305a9d298b52143c43c27a346e605f9a975a1f20 Mon Sep 17 00:00:00 2001 From: Alex Cabal Date: Mon, 20 Jun 2022 13:52:39 -0500 Subject: [PATCH] Add XSLT stylesheet for OPDS feeds --- lib/OpdsFeed.php | 4 ++ templates/EbookGrid.php | 2 +- templates/OpdsAcquisitionEntry.php | 3 +- templates/OpdsAcquisitionFeed.php | 13 ++-- templates/OpdsNavigationFeed.php | 16 +++-- www/css/core.css | 96 ++++++++++++++++++------------ www/css/dark.css | 2 +- www/feeds/index.php | 7 +-- www/opds/style.php | 93 +++++++++++++++++++++++++++++ www/rss/style.php | 2 +- 10 files changed, 182 insertions(+), 56 deletions(-) create mode 100644 www/opds/style.php diff --git a/lib/OpdsFeed.php b/lib/OpdsFeed.php index c10b8fba..9a8d64d2 100644 --- a/lib/OpdsFeed.php +++ b/lib/OpdsFeed.php @@ -86,6 +86,10 @@ class OpdsFeed{ file_put_contents($parentFilepath, str_replace(" ns=", " xmlns=", $xmlString)); + // If we include this stylsheet declaration in the OPDS template, `se clean` will remove it and also + // add a bunch of empty namespaces in the output. So, add it programatically here instead. + file_put_contents($tempFilename, str_replace("?>", "?>\n", file_get_contents($tempFilename))); + rename($tempFilename, $path); } else{ diff --git a/templates/EbookGrid.php b/templates/EbookGrid.php index b6c7626f..2a137142 100644 --- a/templates/EbookGrid.php +++ b/templates/EbookGrid.php @@ -8,7 +8,7 @@ $collection = $collection ?? null; $ebooks = $ebooks ?? []; ?> - class="list" typeof="schema:BookSeries" about="Url ?>"> +
    typeof="schema:BookSeries" about="Url ?>"> diff --git a/templates/OpdsAcquisitionEntry.php b/templates/OpdsAcquisitionEntry.php index 27fac57b..5559cab9 100644 --- a/templates/OpdsAcquisitionEntry.php +++ b/templates/OpdsAcquisitionEntry.php @@ -4,8 +4,9 @@ Authors as $author){ ?> Name, ENT_QUOTES|ENT_XML1, 'utf-8') ?> - WikipediaUrl !== null){ ?>WikipediaUrl, ENT_QUOTES|ENT_XML1, 'utf-8') ?> + AuthorsUrl, ENT_QUOTES|ENT_XML1, 'utf-8') ?> FullName !== null){ ?>FullName, ENT_QUOTES|ENT_XML1, 'utf-8') ?> + WikipediaUrl !== null){ ?>WikipediaUrl, ENT_QUOTES|ENT_XML1, 'utf-8') ?> NacoafUrl !== null){ ?>NacoafUrl, ENT_QUOTES|ENT_XML1, 'utf-8') ?> diff --git a/templates/OpdsAcquisitionFeed.php b/templates/OpdsAcquisitionFeed.php index e3e620ce..071480bd 100644 --- a/templates/OpdsAcquisitionFeed.php +++ b/templates/OpdsAcquisitionFeed.php @@ -10,15 +10,18 @@ $isCrawlable = $isCrawlable ?? false; +// Note that the XSL stylesheet gets stripped during `se clean` when we generate the OPDS feed. +// `se clean` will also start adding empty namespaces everywhere if we include the stylesheet declaration first. +// We have to add it programmatically when saving the OPDS file. print("\n"); ?> xmlns:fh="http://purl.org/syndication/history/1.0"> - - - - - + + + + + <?= htmlspecialchars($title, ENT_QUOTES|ENT_XML1, 'utf-8') ?> Free and liberated ebooks, carefully produced for the true book lover. /images/logo.png diff --git a/templates/OpdsNavigationFeed.php b/templates/OpdsNavigationFeed.php index dd2dd5bb..2d4d2329 100644 --- a/templates/OpdsNavigationFeed.php +++ b/templates/OpdsNavigationFeed.php @@ -7,15 +7,19 @@ - The element is required to note this as a "Complete Acquisition Feeds"; see https://specs.opds.io/opds-1.2#25-complete-acquisition-feeds */ + +// Note that the XSL stylesheet gets stripped during `se clean` when we generate the OPDS feed. +// `se clean` will also start adding empty namespaces everywhere if we include the stylesheet declaration first. +// We have to add it programmatically when saving the OPDS file. print("\n"); ?> - + - - - - - + + + + + <?= htmlspecialchars($title, ENT_QUOTES|ENT_XML1, 'utf-8') ?> Free and liberated ebooks, carefully produced for the true book lover. /images/logo.png diff --git a/www/css/core.css b/www/css/core.css index 74eb92fa..fc539ac4 100644 --- a/www/css/core.css +++ b/www/css/core.css @@ -1186,7 +1186,22 @@ main.ebooks > aside.alert + ol{ margin-top: 2rem; } -main.ebooks > ol{ +.opds ol.ebooks-list.list{ + margin-top: 4rem; +} + +.opds .download{ + font-weight: bold; + margin-top: 1rem; +} + + +.opds .download + ul, +.opds .download + ul > li{ + margin-top: 0; +} + +ol.ebooks-list.grid{ display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); grid-gap: 4rem; @@ -1195,7 +1210,7 @@ main.ebooks > ol{ list-style: none; } -main.ebooks > ol.list{ +ol.ebooks-list.list{ display: flex; flex-direction: column; gap: 0; @@ -1205,50 +1220,54 @@ main.ebooks > ol.list{ max-width: 30rem; } -main.ebooks > ol > li{ +ol.ebooks-list > li{ margin: 0; text-align: center; } -main.ebooks > ol.list > li{ +ol.ebooks-list.list > li{ display: grid; grid-template-columns: 6rem 1fr; grid-column-gap: 1rem; grid-template-rows: auto auto 1fr; } -main.ebooks > ol.list > li + li { +ol.ebooks-list.list > li + li { margin-top: 2rem; } -main.ebooks > ol.list > li > a{ +ol.ebooks-list.list > li > a{ grid-row: 1 / span 3; } -main.ebooks > ol.list ul.tags{ +.opds ol.ebooks-list.list > li > a{ + grid-row: 1 / span 5; +} + +ol.ebooks-list.list ul.tags{ display: flex; justify-content: flex-start; margin-left: -.25rem; margin-top: .25rem; } -main.ebooks > ol.list ul.tags li{ +ol.ebooks-list.list ul.tags li{ margin-left: .25rem; margin-top: .25rem; } -main.ebooks > ol.list > li p{ +ol.ebooks-list.list > li p{ text-align: left; } -main.ebooks > ol a[tabindex]{ +ol.ebooks-list a[tabindex]{ display: inline-block; transition: all .2s ease; position: relative; font-size: 0; } -main.ebooks > ol a[tabindex][data-ordinal]::before{ +ol.ebooks-list a[tabindex][data-ordinal]::before{ display: block; content: "№ "attr(data-ordinal); position: absolute; @@ -1265,29 +1284,29 @@ main.ebooks > ol a[tabindex][data-ordinal]::before{ color: var(--dark-body-text); } -main.ebooks > ol a[tabindex]:hover{ +ol.ebooks-list a[tabindex]:hover{ transform: scale(1.05); color: unset; } -main.ebooks > ol a[tabindex]:hover img{ +ol.ebooks-list a[tabindex]:hover img{ box-shadow: 3px 3px 1px rgba(0, 0, 0, .25); } -main.ebooks > ol > li a[tabindex]:active{ +ol.ebooks-list > li a[tabindex]:active{ transform: scale(1.025); transition: none; box-shadow: none; } -main.ebooks > ol > li p{ +ol.ebooks-list > li p{ margin: 0; text-align: center; -webkit-hyphens: none; hyphens: none; } -main.ebooks > ol > li img{ +ol.ebooks-list > li img{ box-sizing: border-box; max-width: 100%; height: auto; @@ -1295,24 +1314,27 @@ main.ebooks > ol > li img{ border-radius: .25rem; } -main.ebooks > ol > li > p:nth-of-type(1) > a{ +ol.ebooks-list > li > p:nth-of-type(1) > a{ font-weight: bold; text-decoration: none; display: flex; justify-content: center; } -main.ebooks > ol.list > li p.author a, -main.ebooks > ol.list > li > p:nth-of-type(1) > a{ +ol.ebooks-list.list > li p.author a, +ol.ebooks-list.list > li > p:nth-of-type(1) > a{ display: inline; } -main.ebooks > ol > li > a:first-child{ +ol.ebooks-list > li > a:first-child{ font-size: 0; /* for correct focus outline */ } -main.ebooks > ol > li p.author a{ +ol.ebooks-list > li p.author{ font-style: italic; +} + +ol.ebooks-list > li p.author a{ text-decoration: none; display: flex; justify-content: center; @@ -1376,11 +1398,6 @@ figure{ margin-top: 1rem; } -.rss ul.tags + p{ - font-style: italic; - margin-top: 0; -} - ul.tags{ list-style: none; display: flex; @@ -1485,7 +1502,7 @@ p.no-results{ text-align: center; } -.us-pd-warning{ +.s-pd-warning{ font-style: italic; margin-top: 1rem; } @@ -2479,8 +2496,13 @@ ul.feed p{ margin: 0; } -ul.feed p:last-child{ - font-style: italic; +.url{ + font-family: "Fira Mono", monospace; + font-size: .8rem; +} + +.rss > li p:last-child{ + margin-top: 0; } @media (hover: none) and (pointer: coarse){ /* target ipads and smartphones without a mouse */ @@ -2655,7 +2677,7 @@ ul.feed p:last-child{ } @media(max-width: 900px){ - main.ebooks > ol{ + ol.ebooks-list.grid{ grid-template-columns: repeat(3, minmax(0, 1fr)); } @@ -2751,7 +2773,7 @@ ul.feed p:last-child{ } @media(max-width: 700px){ - main.ebooks > ol{ + ol.ebooks-list.grid{ grid-template-columns: repeat(2, minmax(0, 1fr)); } @@ -2995,7 +3017,7 @@ ul.feed p:last-child{ } @media(max-width: 470px){ - main.ebooks > ol{ + ol.ebooks-list.grid{ grid-gap: 2rem; } @@ -3047,7 +3069,7 @@ ul.feed p:last-child{ hyphens: auto; } - main.ebooks > ol{ + ol.ebooks-list.grid{ grid-template-columns: 1fr; } @@ -3101,11 +3123,11 @@ ul.feed p:last-child{ padding: 1rem; } - main.ebooks > ol.list > li{ + ol.ebooks-list.list > li{ grid-template-columns: 1fr; } - main.ebooks > ol.list > li img{ + ol.ebooks-list.list > li img{ max-width: 50%; margin-bottom: 1rem; } @@ -3141,8 +3163,8 @@ ul.feed p:last-child{ article.ebook section#sources ul li a[class]::before, article.ebook #more-ebooks img, article.ebook #more-ebooks a:active img, - main.ebooks > ol a[tabindex], - main.ebooks > ol > li a[tabindex]:active, + ol.ebooks-list a[tabindex], + ol.ebooks-list > li a[tabindex]:active, label.select > span + span::after, label.search:focus-within::before, label.search:hover::before, diff --git a/www/css/dark.css b/www/css/dark.css index c65724bf..e515df6b 100644 --- a/www/css/dark.css +++ b/www/css/dark.css @@ -64,7 +64,7 @@ input::placeholder{ color: rgba(255, 255, 255, .75); } -main.ebooks > ol img:hover{ +ol.ebooks-list img:hover{ box-shadow: 3px 3px 1px rgba(0, 0, 0, .5); } diff --git a/www/feeds/index.php b/www/feeds/index.php index 58b568e6..5f9027b5 100644 --- a/www/feeds/index.php +++ b/www/feeds/index.php @@ -19,7 +19,7 @@ require_once('Core.php');
    • New releases (RSS 2.0)

      -

      /rss/new-releases

      +

      /rss/new-releases

      A list of the thirty latest Standard Ebooks ebook releases, most-recently-released first.

    @@ -29,9 +29,8 @@ require_once('Core.php');

    OPDS feeds are designed for use with ereading systems like KOreader or Calibre, or with ereaders like Foliate. They allow you to search, browse, and download from our catalog, directly in your ereader.

    diff --git a/www/opds/style.php b/www/opds/style.php new file mode 100644 index 00000000..fac6bedc --- /dev/null +++ b/www/opds/style.php @@ -0,0 +1,93 @@ +\n") +?> + + + + false]) ?> +
    +

    +

    This page is an OPDS feed. The URL in your browser’s address bar () can be used in any OPDS client.

    + +
      + +
    1. + + + + + + +

      + +

      +
    2. +
      +
    +
    + +
      + +
    1. + + + + + The cover for the Standard Ebooks edition of Winnie-the-Pooh, by A. A. Milne + + + + + +

      + + + + + + +

      +
      + +

      + + + + + + +

      +
      +
      +
      +

      + +

      +
      +

      Download

      + +
    2. +
      +
    +
    +
    + +
    +
    diff --git a/www/rss/style.php b/www/rss/style.php index 544b8510..26d1ff86 100644 --- a/www/rss/style.php +++ b/www/rss/style.php @@ -12,7 +12,7 @@ print("\n")

    -

    This page is an RSS feed. The URL in your address bar () can be used in any RSS reader.

    +

    This page is an RSS feed. The URL in your browser’s address bar () can be used in any RSS reader.