From ee97f35e280b5044d0ec0f0966dc1473f673c5aa Mon Sep 17 00:00:00 2001 From: Andy Damevin Date: Fri, 17 Jan 2025 14:17:19 +0100 Subject: [PATCH] Simplify relation and linking between pages (#377) --- blog/content/docs/advanced.adoc | 15 +++++-- blog/content/docs/basics.adoc | 40 +++++++++++++++---- .../deployment/RoqPluginTaggingProcessor.java | 2 +- .../RoqFrontMatterPublishProcessor.java | 4 +- .../scan/RoqFrontMatterScanProcessor.java | 2 +- .../deployment/TemplateLinkTest.java | 6 +-- .../runtime/model/DocumentPage.java | 21 ++++++++++ .../roq/frontmatter/runtime/model/Page.java | 3 +- .../frontmatter/runtime/model/PageInfo.java | 6 +-- .../roq/frontmatter/runtime/model/Site.java | 29 ++++++++++---- .../posts/2010-08-05-hello-world/index.md | 5 ++- .../resources/content/\303\251lo you$@.html" | 1 + .../test/java/io/quarkiverse/roq/RoqTest.java | 13 ++++++ 13 files changed, 118 insertions(+), 29 deletions(-) create mode 100644 "roq/integration-tests/src/main/resources/content/\303\251lo you$@.html" diff --git a/blog/content/docs/advanced.adoc b/blog/content/docs/advanced.adoc index deeea61e..459add7d 100644 --- a/blog/content/docs/advanced.adoc +++ b/blog/content/docs/advanced.adoc @@ -318,14 +318,14 @@ The pages links are automatically converted to urls by Roq, they are available i Back to main page ---- -or +or to get the next page url in a document: [source,html] ---- -{page.nextPage.title} +{page.next.title} ---- -or +or when iterating on documents: [source,html] ---- @@ -334,6 +334,15 @@ or {/for} ---- +or also to manually retrieve a page url with `site.page(sourcePath)`: + +[source,html] +---- +{site.page('foo.html').title} +---- + + + TIP: By default, url will be rendered as relative from the site root. You can also get the full absolute url (i.e. from `http(s)://`) by using `absolute` on any url (e.g. `{site.url.absolute}`). === Manual linking diff --git a/blog/content/docs/basics.adoc b/blog/content/docs/basics.adoc index 74521aa0..49730f5f 100644 --- a/blog/content/docs/basics.adoc +++ b/blog/content/docs/basics.adoc @@ -149,13 +149,29 @@ You can use Qute to access site and pages data. For this use the `site` and `pag | `site.image` | `RoqUrl` -| The site image URL if present +| The cover image URL of the page with disk check | `http://example.com/static/images/site.png` -| `site.url(Object path, Object... others)` +| `site.image(String relativePath)` +| `RoqUrl` +| The image from the public images directory with disk check +| `site.image('foo.jpg') => http://example.com/images/foo.jpg` + +| `site.file(String relativePath)` +| `RoqUrl` +| The file from the public directory with disk check +| `site.file('foo.pdf') => http://example.com/foo.pdf` + +| `site.url(String path, String... others)` | `RoqUrl` | Shortcut for site.url.resolve(path) -| `site.url.resolve("/about") => http://example.com/my-roq-site/about` +| `site.url("/about") => http://example.com/my-roq-site/about` + +| `site.page(String sourcePath)` +| `Page` +| Get a page or document page by source path (e.g. pages/first-page.html) +| `site.page('foo.html').url.absolute => http://example.com/the-foo-page` + |=== ==== @@ -203,12 +219,22 @@ You can use Qute to access site and pages data. For this use the `site` and `pag | The description of the page (shortcut from FM) | `This is the about us page.` -| `page.image()` +| `page.image` | `RoqUrl` -| The cover image URL of the page (with disk check) +| The cover image URL of the page with disk check | `http://example.com/static/images/about.png` -| `page.date()` +| `page.image(String relativePath)` +| `RoqUrl` +| The image from the attached files (for index pages) or from the public image directory with disk check (for other pages) +| `page.image('foo.jpg') => http://example.com/foo-page/foo.jpg` + +| `site.file(String relativePath)` +| `RoqUrl` +| The file from the attached files with disk check +| `page.file('foo.pdf') => http://example.com/foo-page/foo.pdf` + +| `page.date` | `ZonedDateTime` | The publication date of the page | `2023-10-01T12:00:00Z` @@ -431,7 +457,7 @@ The resulting link for a page can be different from its directory name, attached Let's imagine for a minute that the page link is `https://my-site.org/awesome-page/`, then the slide will be served on `https://my-site.org/awesome-page/slide.pdf`. -You can use `{page.file("slide.pdf")}` to resolve the file url *and check that the file exists*. This is useful from another page or if you want the absolute url (i.e. `{page.file("slide.pdf").absolute}`): +You can use `{page.file("slide.pdf")}` to resolve the file url *and check that the file exists*. This is also useful in other cases, for example from another page (e.g. `{site.page("my-page/index.md").file("slide.pdf")}`) or if you want the absolute url (e.g. `{page.file("slide.pdf").absolute}`): TIP: If you want to iterate over page files, they can be listed using `{page.files}`. diff --git a/plugin/tagging/deployment/src/main/java/io/quarkiverse/roq/plugin/tagging/deployment/RoqPluginTaggingProcessor.java b/plugin/tagging/deployment/src/main/java/io/quarkiverse/roq/plugin/tagging/deployment/RoqPluginTaggingProcessor.java index 221f0a9d..f22ae996 100644 --- a/plugin/tagging/deployment/src/main/java/io/quarkiverse/roq/plugin/tagging/deployment/RoqPluginTaggingProcessor.java +++ b/plugin/tagging/deployment/src/main/java/io/quarkiverse/roq/plugin/tagging/deployment/RoqPluginTaggingProcessor.java @@ -92,7 +92,7 @@ void publishTagPages( Map.of(":tag", e::getKey)); final RoqUrl url = rootUrl.rootUrl().resolve(link); - PageInfo info = item.raw().info().changeId(tagCollection); + PageInfo info = item.raw().info().changeId(tagCollection + "." + item.raw().info().sourceFileExtension()); // Dealing with pagination is as simple as those two lines: if (data.containsKey(PAGINATE_KEY)) { diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/publish/RoqFrontMatterPublishProcessor.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/publish/RoqFrontMatterPublishProcessor.java index c6fe08bb..037ef5ee 100644 --- a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/publish/RoqFrontMatterPublishProcessor.java +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/publish/RoqFrontMatterPublishProcessor.java @@ -22,6 +22,7 @@ import io.quarkiverse.roq.frontmatter.runtime.model.Paginator; import io.quarkiverse.roq.frontmatter.runtime.model.RootUrl; import io.quarkiverse.roq.frontmatter.runtime.model.RoqUrl; +import io.quarkiverse.roq.util.PathUtils; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.runtime.configuration.ConfigurationException; @@ -92,7 +93,8 @@ public void paginatePublish(RoqSiteConfig config, } PageInfo info = pagination.info(); if (i > 1) { - info = info.changeId(paginatedUrl.path()); + info = info.changeId( + PathUtils.removeExtension(info.sourceFilePath()) + "_p" + i + "." + info.sourceFileExtension()); } paginatedPages.add(new PageToPublish(paginatedUrl, info, data)); } diff --git a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/scan/RoqFrontMatterScanProcessor.java b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/scan/RoqFrontMatterScanProcessor.java index c021fcd9..5131823f 100644 --- a/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/scan/RoqFrontMatterScanProcessor.java +++ b/roq-frontmatter/deployment/src/main/java/io/quarkiverse/roq/frontmatter/deployment/scan/RoqFrontMatterScanProcessor.java @@ -381,7 +381,7 @@ private static Consumer addBuildItem( String quteTemplatePath = ROQ_GENERATED_QUTE_PREFIX + removeExtension(normalizedPath) + resolveOutputExtension(markups, normalizedPath); boolean published = type.isPage(); - String id = type.isPage() ? normalizedPath : removeExtension(normalizedPath); + String id = type.isPage() ? sourcePath : removeExtension(sourcePath); final boolean isHtml = isPageTargetHtml(file); var isIndex = isHtml && "index".equals(PathUtils.removeExtension(PathUtils.fileName(sourcePath))); var isSiteIndex = isHtml && id.startsWith("index."); diff --git a/roq-frontmatter/deployment/src/test/java/io/quarkiverse/roq/frontmatter/deployment/TemplateLinkTest.java b/roq-frontmatter/deployment/src/test/java/io/quarkiverse/roq/frontmatter/deployment/TemplateLinkTest.java index 6a18d677..fe1a5582 100644 --- a/roq-frontmatter/deployment/src/test/java/io/quarkiverse/roq/frontmatter/deployment/TemplateLinkTest.java +++ b/roq-frontmatter/deployment/src/test/java/io/quarkiverse/roq/frontmatter/deployment/TemplateLinkTest.java @@ -16,7 +16,7 @@ class TemplateLinkTest { @Test void testLink() { JsonObject frontMatter = new JsonObject().put("title", "My First Blog Post"); - final PageInfo pageInfo = createPageInfo("_posts/my-first-blog-post.md", "_posts/my-first-blog-post.md", true, true); + final PageInfo pageInfo = createPageInfo("posts/my-first-blog-post.md", "posts/my-first-blog-post.md", true, true); String generatedLink = pageLink("/", ":year/:month/:day/:slug", new PageLinkData(pageInfo, null, frontMatter)); assertEquals("2024/08/27/my-first-blog-post/", generatedLink); @@ -25,7 +25,7 @@ void testLink() { @Test void testLinkExt() { JsonObject frontMatter = new JsonObject().put("title", "My First Blog Post"); - final PageInfo pageInfo = createPageInfo("posts/my-first-blog-post.md", "_posts/my-first-blog-post.md", true, true); + final PageInfo pageInfo = createPageInfo("posts/my-first-blog-post.md", "posts/my-first-blog-post.md", true, true); String generatedLink = pageLink("/", ":year/:month/:day/:slug:ext!", new PageLinkData(pageInfo, null, frontMatter)); assertEquals("2024/08/27/my-first-blog-post.html", generatedLink); @@ -46,7 +46,7 @@ void testLinkJson() { @Test void testPaginateLink() { JsonObject frontMatter = new JsonObject().put("title", "My First Blog Post"); - final PageInfo pageInfo = createPageInfo("posts/my-first-blog-post.html", "_posts/my-first-blog-post.md", true, true); + final PageInfo pageInfo = createPageInfo("posts/my-first-blog-post.html", "posts/my-first-blog-post.md", true, true); String generatedLink = paginateLink("/", null, new PaginateLinkData(pageInfo, "posts", "3", frontMatter)); assertEquals("posts/page3/", generatedLink); diff --git a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/DocumentPage.java b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/DocumentPage.java index cd122a5d..777991f6 100644 --- a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/DocumentPage.java +++ b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/DocumentPage.java @@ -35,6 +35,27 @@ public DocumentPage nextPage() { return collection().nextPage(this); } + /** + * Resolve the next document page in the collection or null if none + */ + public DocumentPage next() { + return nextPage(); + } + + /** + * Resolve the previous document page in the collection or null if none + */ + public DocumentPage previous() { + return previousPage(); + } + + /** + * Resolve the previous document page in the collection or null if none + */ + public DocumentPage prev() { + return previousPage(); + } + /** * Resolve the previous document page in the collection or null if none */ diff --git a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Page.java b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Page.java index d35a9688..7937d348 100644 --- a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Page.java +++ b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Page.java @@ -29,7 +29,8 @@ public interface Page { PageInfo info(); /** - * The page unique id, it is either the source file name (e.g. _posts/my-post.md or a generated id for dynamic pages). + * The page unique identifier, it is either the source file relative path (e.g. posts/my-post.md or a generated source path + * for dynamic pages). */ default String id() { return info().id(); diff --git a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/PageInfo.java b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/PageInfo.java index ad2424ff..1bc10150 100644 --- a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/PageInfo.java +++ b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/PageInfo.java @@ -14,8 +14,8 @@ @Vetoed public record PageInfo( /** - * The page unique id, it is either the source file name (e.g. _posts/my-favorite-beer.md or a generated id for dynamic - * pages). + * The page unique identifier, it is either the source file relative path (e.g. posts/my-post.md or a generated source + * path for dynamic pages). */ String id, @@ -35,7 +35,7 @@ public record PageInfo( String rawContent, /** - * The path of the source file (e.g _posts/my-favorite-beer.md) + * The path of the source file (e.g posts/my-favorite-beer.md) */ String sourceFilePath, diff --git a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Site.java b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Site.java index d5305072..a6a540e8 100644 --- a/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Site.java +++ b/roq-frontmatter/runtime/src/main/java/io/quarkiverse/roq/frontmatter/runtime/model/Site.java @@ -146,23 +146,36 @@ public RoqUrl url(Object path, Object path1, Object path2) { } /** - * Find a page by id + * Find a page or document page by source path * - * @param id the page id (e.g. pages/first-page.html) + * @param sourcePath the page source path (e.g. pages/first-page.html) or the generated source path for generated pages (e.g + * /index_p2.html). * @return the page or null */ - public NormalPage page(String id) { - return pagesById.get().get(id); + public Page page(String sourcePath) { + return pagesById.get().containsKey(sourcePath) ? pagesById.get().get(sourcePath) : documentsById.get().get(sourcePath); } /** - * Find a document by id + * Find a normal page (documents not included) by source path * - * @param id the document page id (e.g. _posts/first-post) + * @param sourcePath the page source path (e.g. pages/first-page.html) or the generated source path for generated pages (e.g + * /index_p2.html). + * @return the page or null + */ + public NormalPage normalPage(String sourcePath) { + return pagesById.get().get(sourcePath); + } + + /** + * Find a document by sourcePath + * + * @param sourcePath the document source path (e.g. pages/first-page.html) or the generated source path for generated + * documents. * @return the document page or null */ - public DocumentPage document(String id) { - return documentsById.get().get(id); + public DocumentPage document(String sourcePath) { + return documentsById.get().get(sourcePath); } public RoqUrl url() { diff --git a/roq/integration-tests/src/main/resources/content/posts/2010-08-05-hello-world/index.md b/roq/integration-tests/src/main/resources/content/posts/2010-08-05-hello-world/index.md index 3728e752..a0adcbf2 100644 --- a/roq/integration-tests/src/main/resources/content/posts/2010-08-05-hello-world/index.md +++ b/roq/integration-tests/src/main/resources/content/posts/2010-08-05-hello-world/index.md @@ -2,4 +2,7 @@ Here are the links: {page.file('hello.pdf')} and {page.file('./hello.pdf')} -and an images: {site.image('hello.png')} and {page.image('hello-page.png')} and {page.image('./hello-page.png')}; \ No newline at end of file +and an images: {site.image('hello.png')} and {page.image('hello-page.png')} and {page.image('./hello-page.png')} + +page by path: {site.page('élo you$@.html').url} +document by path: {site.document('posts/markdown-post-k8s.md').url} \ No newline at end of file diff --git "a/roq/integration-tests/src/main/resources/content/\303\251lo you$@.html" "b/roq/integration-tests/src/main/resources/content/\303\251lo you$@.html" new file mode 100644 index 00000000..b1c9a6df --- /dev/null +++ "b/roq/integration-tests/src/main/resources/content/\303\251lo you$@.html" @@ -0,0 +1 @@ +{site.page('élo you.html').url} \ No newline at end of file diff --git a/roq/integration-tests/src/test/java/io/quarkiverse/roq/RoqTest.java b/roq/integration-tests/src/test/java/io/quarkiverse/roq/RoqTest.java index 361f8d31..830a1682 100644 --- a/roq/integration-tests/src/test/java/io/quarkiverse/roq/RoqTest.java +++ b/roq/integration-tests/src/test/java/io/quarkiverse/roq/RoqTest.java @@ -57,6 +57,19 @@ public void testMdPost() { .body(containsString("Legacy: /static/assets/images/legacy.png")); } + @Test + public void testPageDir() { + RestAssured.when().get("/posts/2010-08-05-hello-world").then().statusCode(200).log().ifValidationFails() + .body("html.head.title", equalTo("posts/2010-08-05-hello-world/index.md - Hello, world! I'm Roq")) + .body(containsString("

posts/2010-08-05-hello-world/index.md")) + .body(containsString( + "Here are the links: /posts/2010-08-05-hello-world/hello.pdf and /posts/2010-08-05-hello-world/hello.pdf")) + .body(containsString( + "and an images: /images/hello.png and /posts/2010-08-05-hello-world/hello-page.png and /posts/2010-08-05-hello-world/hello-page.png")) + .body(containsString("page by path: /lo-you/")) + .body(containsString("document by path: /posts/k8s-post/")); + } + @Test public void testCodestartPost() { RestAssured.when().get("/posts/the-first-roq").then().statusCode(200).log().ifValidationFails()