Images and URLs

Contents

Guillotine provides URL components for pages, scaled images, media and attachments. Select path and queryString, then build links using your client’s media base URL or frontend routing.

The components come from XP’s pageUrlParts, imageUrlParts and attachmentUrlParts, whose reference describes each of them. The URL fields are Portal API extensions.

These examples query published content in myproject. Replace the project and content paths with your own, and send the queries using the request format on the Usage page.

Scaled images

On media_Image content, imageUrl provides the path for a requested size and crop, or for the image as stored without a scale. Guillotine mounted on a site or a webapp gives the image as stored only, unless configured otherwise; see Image transformations. This query finds up to ten images and requests an 800 by 200 pixel crop for each:

{
  guillotine(project: "myproject", branch: "master") {
    queryDsl(
      query: {
        term: {
          field: "type"
          value: { string: "media:image" }
        }
      }
      first: 10
    ) {
      displayName
      ... on media_Image {
        imageUrl(scale: "block(800,200)") {
          path
          queryString
        }
      }
    }
  }
}

Prefix the returned path with your media base URL and append queryString. With the shared API base URL https://example.com/api, the resulting image address can be:

https://example.com/api/media:image/myproject/contentid:fingerprint/block-800-200/photo.jpg

Media and attachments

Media content exposes mediaUrl for its binary attachment. Other attachments are available through the content item’s attachments field. Select path and queryString on attachmentUrl to build an attachment link:

{
  guillotine(project: "myproject", branch: "master") {
    get(key: "/my-site/downloads/report") {
      attachments {
        name
        attachmentUrl(download: true) {
          path
          queryString
        }
      }
      ... on media_Document {
        mediaUrl(download: true) {
          path
          queryString
        }
      }
    }
  }
}

This example assumes a document media item. The download argument is carried by queryString, so retain it when building the link. See AttachmentUrl for the returned fields.

Page URLs

Select pageUrl.path and pageUrl.queryString to build a content item’s page link. scopeKey names the site, or another content, that the path is relative to; without it, the path is relative to the project:

{
  guillotine(project: "myproject", branch: "master", scopeKey: "/my-site") {
    get(key: "/my-site/posts/first-post") {
      pageUrl {
        path
        queryString
      }
    }
  }
}

The returned path is /posts/first-post. Select baseUrl as well to receive the Base URL configured for /my-site, or null when it has none. Adapt the path to your frontend’s routes: a frontend serving the site at /news could use /news/posts/first-post, while one on a dedicated host could use https://www.example.com/posts/first-post. Append queryString to preserve any parameters.

Base URLs

Your frontend controls the public host and routing for page links. Use scopeKey to select the site, or another content, that pageUrl.path is relative to, then map that path to your deployment. Without scopeKey, the project is selected.

For nested sites, the selected level determines the path. Given a post at /my-site/subsite/posts/first-post:

scopeKey Page URL path of the post Page URL path of /my-site/subsite

/my-site

/subsite/posts/first-post

/subsite

/my-site/subsite

/posts/first-post

(empty)

/

/my-site/subsite/posts/first-post

/my-site/subsite

(none)

/my-site/subsite/posts/first-post

/my-site/subsite

When your frontend needs a path of its own for the site root, use / for an empty path.

scopeKey: "/", like no scopeKey, selects the project itself. pageUrl.baseUrl is the Base URL configured for the selected site or project, followed by the path of a selected content below it, so that baseUrl + path is the page’s URL; see XP’s pageUrlParts.

A content outside the scope, such as the parent site when the nested one is selected, has no page URL there, and pageUrl reports an error.

When following references across sites, choose the appropriate site context and frontend route for each target.

XP serves images and attachments at /api/media:image/…​ and /api/media:attachment/…​ by default. When your deployment exposes the shared API base URL, it provides routes for both Guillotine and these media APIs. For example, an API base of https://example.com/api serves GraphQL at /com.enonic.app.guillotine:graphql and images at /media:image/…​ beneath that base.

Configure a media base URL in your client and prefix it to the returned media paths. Use the shared API base, or a separate CDN base such as https://cdn.example.com/api. The CDN must route those paths to the corresponding XP media endpoints. If images and attachments use different hosts, configure a base for each.

The media base URL is yours to supply. See Media base URL.

In processed rich text, links and images are placeholders, which your client rewrites from the structured links and images data.

Serving media from a CDN host

A CDN host can serve the media API paths without their leading segments, such as https://cdn.example.com/myproject/d1:5ab2…/price-list.pdf without /media:attachment, or https://media.myproject.com/d1:5ab2…/price-list.pdf without the context as well. Set up such a host with an XP virtual host mapping, and build its links from the segments of the URL components; see Building URLs from segments.

Building URLs from parts

The pageUrl, imageUrl, mediaUrl and attachmentUrl fields expose path and queryString, ready to use as they are. The params argument of pageUrl adds query parameters to queryString: params: { page: 2, tag: ["a", "b"], preview: null } gives ?page=2&tag=a&tag=b&preview. For a deployment that preserves the returned paths, assemble links as baseUrl + path + queryString.

pageUrl.baseUrl can serve as the base of page links. It is null when none is configured, so check it before concatenating: in JavaScript, null + path produces the string "null/…​" without an error.

Choose a base without a trailing slash. For media, use the API or CDN base that serves the media API paths. For pages, supply your frontend’s host or route prefix, or adapt the path to your routing scheme.

const mediaBaseUrl = 'https://example.com/api';
const imageSrc = mediaBaseUrl + image.imageUrl.path + image.imageUrl.queryString;

// The site's configured Base URL when it has one; this frontend serves the site's pages under /news otherwise.
const pageBase = content.pageUrl.baseUrl ?? '/news';
const pageHref = pageBase + content.pageUrl.path + content.pageUrl.queryString;
{
  guillotine(project: "myproject", branch: "master") {
    get(key: "/my-site/images/photo") {
      ... on media_Image {
        imageUrl(scale: "block(800,200)") {
          path
          queryString
          fingerprint
        }
      }
    }
  }
}

For example, path can be /media:image/myproject/contentid:fingerprint/block-800-200/photo.jpg.

Building URLs from segments

imageUrl, mediaUrl and attachmentUrl also expose the segments path is made of, described with XP’s imageUrlParts and attachmentUrlParts. Use them when a CDN host hides leading segments:

mediaUrl(download: true) {
  queryString
  context
  id
  fingerprint
  name
}
const u = content.mediaUrl;  // or link.media.mediaUrl in rich text
const i = content.imageUrl;  // or image.src in rich text

// <id>:<fingerprint>, or the id alone when there is no fingerprint
const idHash = (u) => u.fingerprint ? `${u.id}:${u.fingerprint}` : u.id;

// the API segment hidden by the CDN host
const href = `https://cdn.example.com/${u.context}/${idHash(u)}/${u.name}${u.queryString}`;
// the context segment hidden too
const href2 = `https://media.myproject.com/${idHash(u)}/${u.name}${u.queryString}`;
// images carry the scale segment before the name
const src = `https://img.example.com/${i.context}/${idHash(i)}/${i.scale}/${i.name}${i.queryString}`;

Use the segments as they are returned, including the fingerprint. See PageUrl, ImageUrl and AttachmentUrl for the available components.

Use each image URL as it is returned, and don’t make other sizes by editing its scale segment. The fingerprint of an image URL can depend on its whole transformation, so an edited URL may no longer be served. The image’s style also contributes to the scale. Ask for each size with the scale argument of imageUrl, or with imageSrcWidth and imageWidths for rich text, instead.

Contents

Contents