Skip to content

lsst-epo/canto-dam-assets

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Canto DAM Assets

Screenshot

This Craft CMS plugin adds a Field Type with GraphQL support for the Canto Digital Asset Management (DAM) web system.

Overview

This plugin works by leveraging a modified version of the Canto Universal Connector to provide the asset picker for your Canto library inside of Craft CMS.

Screenshot

It stores all of the selected Canto asset data in Craft CMS as a JSON blob, which can be accessed via Twig or GraphQL.

The Canto asset data is represented as a Laravel Collection, allowing you to use the various Collection methods to search, sort, filter, etc. the data.

Requirements

This plugin requires Craft CMS 4.4.0 or later, and PHP 8.0.2 or later.

Configuring

Plugin Settings

Before you can use the Canto DAM Assets plugin, you need to configure it via Settings → Plugins → Canto DAM Assets:

Screenshot

  • App ID - Your Canto DAM Application ID
  • Secret Key - Your Canto DAM secret key
  • Authentication Endpoint - The URL that should be used to authenticate and obtain an access token from. You can include the {appId} & {secretKey} tokens in the URL, which will be replaced with the respective values from the settings
  • Tenant Host Name - The hostname for the Canto Tenant, e.g. rubin.canto.com
  • Webhook Secure Token - The secure token that must match the token coming from the Canto webhook Secure Token field to allow changes to be synced from Canto

Field Settings

Each Canto DAM Assets field can be configured via its Field Settings, to control what Canto assets can be chosen by the field:

Screenshot

  • Single Image - Only a single image can be picked for use in the field
  • Multiple Images - Either a single image, or multiple images (via the round checkbox in the upper-right corner of the image) can be selected
  • Entire Album - Only an entire album of images can be selected

You can have multiple Canto DAM Asset fields for any given entry, each with their own separate Field Settings.

Since the Canto Asset Picker Type is just a field setting, admins can at any time change what the content author is allowed to pick. Changing this setting for an existing field does not change the assets stored in the field, it just alters what UX is presented to the content author in the future.

Content Authoring With The Canto DAM Asset field

You can have multiple Canto DAM Asset fields in any entry in Craft CMS:

Screenshot

If a single image is selected, a preview of it will appear with the name of the image, and the album it comes from.

If you have multiple images selected (such as for an entire album of images), a preview of the first image will appear with the number of images and the album they come from, with a stack below it as a visual cue that there are multiple images.

Clicking on Remove Images will remove the images from the field.

Clicking on Choose a Different DAM Asset will bring up the Canto Universal Connector UX the allows you to choose the asset(s) to use:

Single Asset

To choose a single asset, click on the image for a detail view, when click on Confirm to use the image:

Screenshot

Multiple Assets

To select multiple assets, click on the circle in the upper-right corner of each image (a âś“ will appear), and then click on the Insert button:

And Entire Album

To select an entire album, click on the album in the list view on the left, then click on the Insert Album button:

Screenshot

Canto DAM Field Data Structure

Just like Assets in Craft CMS, each Canto DAM Field stores data for an array of n assets. So a single Canto DAM Asset is treated the same as a gallery of 10 Canto DAM Assets.

The data the Canto DAM Asset field type stores is as follows, each in their own separate table in the content table of the Craft CMS database. The separate content table columns allows for easy searching based on the cantoId or cantoAlbumId which are broken out of their respective data structures:

  • cantoId - The id of the Canto Asset, or 0 if this is a collection of images
  • cantoAlbumId - The id of the Canto Album
  • cantoAssetData - A JSON blob that is an array of Canto DAM Assets
  • cantoAlbumData - A JSON blob of Canto DAM Album data

Canto DAM Asset Data Structure

The selected Canto DAM Asset data is stored as a JSON blob with a structure that mirrors the results from the batch/content endpoint combined with the data from the undocumented batch/directuri endpoint.

Below are the available fields for the Canto Asset Data of the scheme image; the fields may vary for other asset types:

{
  id
  uid
  metadata {
    BitsPerPixel
    FileTypeDetail
    FileTypeExtension
    FlightFileExtension
    GIFVersion
    BackgroundColor
    AnimationIterations
    WHRotated
    RSize
    FlightFileType
    FileName
    ColorResolutionDepth
    Comment
    CreateDate
    FileInodeChangeDateTime
    FileType
    ImageSize
    FileAccessDateTime
    ImageHeight
    Orientation
    ImageWidth
    DurationTime
    FileModificationDateTime
    MIMEType
    FinfotoolVersion
    AssetDataSizeLong
    Megapixels
    HasColorMap
    FrameCount
    Panoramas
  }
  height
  relatedAlbums {
    id
  }
  md5
  approvalStatus
  ownerName
  smartTags
  dpi
  lastUploaded
  versionHistory {
    no
    ownerName
    created
    time
    version
    comment
    currentVersion
  }
  created
  keyword
  time
  tag
  additional {
    Description
    UploadedBy
    WebDAMGroupID
    SpatialReferenceValue
    SpatialCoordinateSystemProjection
    MetadataVersion
    UploaderContact
    Credit
    WebDAMPublisherID
    AltTextES
    PublisherID
    SpatialReferenceDimension
    WebDAMPublisher
    AltTextEN
    ID
    SocialMediaDescription
    TitleES
    TitleEN
    MediaConsent
    SpatialRotation
    Title
    Publisher
    SpatialScale
    SpatialReferencePixel
    WebDAMSublocation
    SpatialCoordinateFrame
    CaptionES
    Type
    SocialMediaHandles
    CaptionEN
    UsageTerms
    WebDAMMediaType
  }
  url {
    preview
    download
    metadata
    HighJPG
    PNG
    directUrlOriginal
    detail
    directUrlPreview
    directUrlPreviewPlay
    LowJPG
  }
  width
  name
default {
    Size
    UploadedBy
    Dimensions
    GPS
    DateUploaded
    DateModified
    Name
    Copyright
    ModifiedBy
    LowJPG
    ContentType
    Author
    DateCreated
    Resolution
  }
  size
  scheme
  owner
}

You can use the GraphiQL IDE built into Craft CMS to explore the data structure interactively.

Using Twig

You can access the data stored in a Canto DAM Asset field type via Twig by accessing it as you would any other field type. In the examples below, someDamAsset is the field handle of a Canto DAM Asset field:

{{ entry.someDamAsset.cantoAssetData.first().url.directUrlOriginal }}

Note that cantoAssetData is a Laravel Collection that contains an array of Canto DAM Assets, so we are using the .first() method to get the first item from the array, and then we are accessing the url.directUrlOriginal of the Canto DAM Asset data structure.

Since cantoAssetData is a Laravel Collection, all Collection methods are available to operate on the Canto DAM Asset data.

Craft CMS also provides a .one() method that aliases to .first(), so you can use that as well to mirror how Element Queries work, if you like.

Using GraphQL

You can also access the data stored in a Canto DAM Asset field type via Craft CMS's GraphQL API inside of an Entry query.

In the examples below, someDamAsset is the field handle of a Canto DAM Asset field:

Simple Queries

{
  entry(section: "homepage") {
    ... on homepage_homepage_Entry {
      someDamAsset {
        id,
        url {
          directUrlOriginal
        }
      }
    }
  }
}

For a Canto DAM Assets field that has one asset, the response will look like this:

{
  "data": {
    "entry": {
      "someDamAsset": [
        {
          "id": "vjbnb7df8t6b3al3sumpo66s0n",
          "url": {
            "directUrlOriginal": "https://Example.canto.com/direct/image/vjbnb7df8t6b3al3sumpo66s0n/ZgZdSVOt9ZxsSjf1AKd4ficjBcE/original?content-type=image%2Fjpeg&name=IMG_0599.jpeg"
          }
        }
      ]
    }
  }
}

For a Canto DAM Asset field that contains multiple assets, the response will look like this:

{
  "data": {
    "entry": {
      "someDamAsset": [
        {
          "id": "vjbnb7df8t6b3al3sumpo66s0n",
          "url": {
            "directUrlOriginal": "https://Example.canto.com/direct/image/vjbnb7df8t6b3al3sumpo66s0n/ZgZdSVOt9ZxsSjf1AKd4ficjBcE/original?content-type=image%2Fjpeg&name=IMG_0599.jpeg"
          }
        },
        {
          "id": "docn3e2imd5cncpfk78aatvb1h",
          "url": {
            "directUrlOriginal": "https://Example.canto.com/direct/image/docn3e2imd5cncpfk78aatvb1h/m8MCieLzJVnwfofy5HG9ZSi6qF8/original?content-type=image%2Fjpeg&name=IMG_0790.jpeg"
          }
        },
        {
          "id": "k9qquhbn2l2vn4c8t9jnr5l90e",
          "url": {
            "directUrlOriginal": "https://Example.canto.com/direct/image/k9qquhbn2l2vn4c8t9jnr5l90e/afyd7vgCFoqesmf6JrEejLwHaIM/original?content-type=image%2Fjpeg&name=IMG_0604.jpeg"
          }
        },
        {
          "id": "v7jrfok1r57k5fq20trm8bnt0a",
          "url": {
            "directUrlOriginal": "https://Example.canto.com/direct/image/v7jrfok1r57k5fq20trm8bnt0a/RqGiM6RPSnndSIMlbCy5YeOt4BQ/original?content-type=image%2Fjpeg&name=IMG_0602.jpeg"
          }
        }
      ]
    }
  }
}

In both cases, the data will be a JSON array of Canto DAM Asset data structures.

Complex Queries

You can also treat the Canto DAM Asset data as a database, and do queries based on the content of fields or sub-fields there.

This works by mapping a subset of Collection methods to arguments in GraphQL.

In the examples below, someDamAsset is the field handle of a Canto DAM Asset field.

So for example, this query:

{
  entry(section: "homepage") {
    ... on homepage_homepage_Entry {
      someDamAsset(where: {key: "default.Author", value: "Hernan Stockebrand"}, sortByDesc: "default.Size") {
        id,
        url {
          directUrlOriginal
        }
      }
    }
  }
}

...will return all of the Canto DAM Assets in the someDamAsset field that have the default.Author set to Hernan Stockebrand, sorted in descending order by the default.Size field.

Only a subset of Collection methods are available as arguments in your GraphQL query, because only some Collection methods return Collection data directly.

Query Arguments

Here's a list of the available arguments, and the types they expect as parameters:

  • except: [Int] - Get all items except for those with the specified indexes.

  • first: Boolean - Get the first item from the collection.

  • last: Boolean - Get the last item from the collection.

  • nth: Int - Return a collection consisting of every n-th element.

  • random: Int - Get the specified number of items randomly from the collection.

  • reverse: Boolean - Reverse the list

  • shuffle: Int - Shuffle the items in the collection, using the value as a random number seed.

  • skip: Int - Skip the first N items.

  • sortBy: [String] - Sort the collection using the sort string(s). You can use the field.subField syntax for nested fields and provide multiple sort commands as a list of strings.

  • sortByDesc: [String] - Sort the collection using the sort string(s) in a descending order. You can use the field.subField syntax for nested fields and provide multiple sort commands as a list of strings.

  • forPage: ForPageInput - Paginate the items by page number and items per page.

  • where: WhereFiltersInput - Get all items by the given key value pair, using the optional operator for comparison.

  • whereBetween: WhereBetweenFiltersInput Filter items such that the value of the given key is between the given values.

  • whereIn: WhereInFiltersInput - Filter items such that the value of the given key is in the array of values provided.

  • whereNotBetween: WhereNotBetweenFiltersInput - Filter items such that the value of the given key is NOT between the given values. This argument expects exactly three values in an array. You can use the field.subField syntax for nested fields.

  • whereNotIn: WhereNotInFiltersInput - Filter items by the given key value pair, making sure the value is not in the array.

  • whereNotNull: String - Return items from the collection where the given key is not null. You can use the field.subField syntax for nested fields.

  • whereNull: String - Return items from the collection where the given key is null. You can use the field.subField syntax for nested fields.

Query Argument types

  • ForPageInput - Used with the forPage argument, in the format: {page: 1, items: 10}:

    • page: Int - The page number

    • items: Int - The number of items per page

  • WhereFiltersInput - Used with the where argument, in the format: {key: "key", value: "value", operator: "operator"}:

    • key: String - The key to search on, you can use the field.subField syntax for nested fields

    • value: String - The value to match when searching

    • operator: String - The comparison operator to use, e.g.: =, >, <=, etc. The default is =

  • WhereBetweenFiltersInput - Used with the whereBetween argument, in the format: {key: "key", values: ["value1", "value2"]}:

    • key: String - The key to search on, you can use the field.subField syntax for nested fields

    • values: String - The values that the key should be between

  • WhereInFiltersInput - Used with the whereIn argument, in the format: {key: "key", values: ["value1", "value2"]}:

    • key: String - The key to search on, you can use the field.subField syntax for nested fields

    • values: String - The values that should be in the key

  • WhereNotBetweenFiltersInput - Used with the whereNotBetween argument, in the format: {key: "key", values: ["value1", "value2"]}:

    • key: String - The key to search on, you can use the field.subField syntax for nested fields

    • values: String - The values that the key should not be between

  • WhereNotInFiltersInput - Used with the whereNotIn argument, in the format: {key: "key", values: ["value1", "value2"]}:

    • key: String - The key to search on, you can use the field.subField syntax for nested fields

    • values: String - The values that should not be in the key

You can use the GraphiQL IDE built into Craft CMS to try queries interactively.

Webhook Controller Actions

The Canto DAM Assets plugin has several controller API endpoints, to allow for the syncing of data from Canto webhooks to Craft:

  • _canto-dam-assets/sync/delete-by-canto-id - This will delete a Canto Asset from any Canto DAM Assets fields. Example:
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"album":"N29BC", "displayname":"ADASS Poster 6-23.png", "id":"tsfhf1snqh5533j96gi2ntom7j", "scheme":"image", "secure_token":"abc"}' \
  http://plugindev.local:8004/actions/_canto-dam-assets/sync/delete-by-canto-id
  • _canto-dam-assets/sync/delete-by-album-id - This will delete an entire Canto Album from any Canto DAM Assets fields. Example:
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"album":"N29BC", "displayname":"ADASS Poster 6-23.png", "id":"psnne2p0717un0d115ftjd4l1a", "scheme":"image", "secure_token":"abc"}' \
  http://plugindev.local:8004/actions/_canto-dam-assets/sync/delete-by-album-id
  • _canto-dam-assets/sync/update-by-canto-id - This will update the metadata for a Canto Asset in any Canto DAM Assets fields. Example:
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"album":"N29BC", "displayname":"ADASS Poster 6-23.png", "id":"psnne2p0717un0d115ftjd4l1a", "scheme":"image", "secure_token":"abc"}' \
  http://plugindev.local:8004/actions/_canto-dam-assets/sync/update-by-canto-id
  • _canto-dam-assets/sync/update-by-album-id - This will update and entire Canto Album in any Canto DAM Assets fields. Example:
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"album":"N29BC", "displayname":"ADASS Poster 6-23.png", "id":"psnne2p0717un0d115ftjd4l1a", "scheme":"image", "secure_token":"abc"}' \
  http://plugindev.local:8004/actions/_canto-dam-assets/sync/update-by-album-id

The secure_token setting in each Canto webhook needs to match the Webhook Secure Token plugin setting for it to be considered valid.

Plugin Roadmap

Some things to do, and ideas for potential features:

  • Add preview support for the display of video, PDFs, etc. in the Canto DAM Asset field type
  • Convert the canto-field.js to TypeScript
  • Add tests to the plugin
  • Consider submitting the plugin to the Plugin Store
  • Consider submitting the plugin to the Canto Integrations page

Brought to you by nystudio107