Leoloso PoP Versions Save

Interact with all your data in WordPress using GraphQL

2.3.0

1 week ago

Improvements

  • Added documentation for integration with Polylang (#2664)
  • Added module type "Integrations" (#2665)
  • Return an EnumString type on GenericCategory.taxonomy and GenericTag.taxonomy (#2666)
  • Added predefined persisted queries:
    • [PRO] Translate posts for Polylang (Gutenberg) (#2667)
    • [PRO] Translate posts for Polylang (Classic editor) (#2667)
    • [PRO] Sync featured image for Polylang (#2669)
    • [PRO] Sync tags and categories for Polylang (#2670)

Added fields GenericCustomPost.update, Root.updateCustomPost and Root.createCustomPost (#2663)

We have added mutations to create an update custom posts!

For instance, this query updates the title and content for a Custom Post Type "my-portfolio":

mutation UpdateCustomPost {
  updateCustomPost(input: {
    id: 1616
    customPostType: "my-portfolio"
    title: "Updated title"
    contentAs: { html: "Updated content" }
  }) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    customPost {
      __typename
      ...on CustomPost {
        id
        title
        content
      }
    }
  }
}

Concerning Custom Post Types (CPT) of your own creation, and which do not require any additional fields over those from a Post, then you can use both createCustomPost and updateCustomPost without any fear or restraint. For instance, a MyPortfolio CPT that simply uses the standard fields title and content, and has no extra fields, can be fully managed via these new fields.

Custom post types that are provided by 3rd-party plugins, though, may need to be created (and possibly updated too) by the corresponding plugin only. This is because they may have their custom data (either in wp_postmeta or in a proprietary table) that needs to be added too, and that Gato GraphQL is unaware of.

To manage these CPTs appropriately, a corresponding integration between that plugin and Gato GraphQL should be created, which would provide the mapping for all the fields for the CPT.

For instance, to work with WooCommerce, we can currently use field Root.updateCustomPost to translate and update the title and content of an WooCommerce product (i.e. from the Product CPT).

However, we cannot create an WooCommerce product. For that, we must wait until the "WooCommerce for Gato GraphQL" extension is available.

Support alternative filenames from 3rd-party plugins for extensions (#2671)

Gato GraphQL extensions with 3rd-party plugins now support alternative filenames from the plugin, where any of them being active will make the extension be enabled.

For instance, the Polylang extension in Gato GraphQL PRO will be enabled if either polylang/polylang.php or polylang-pro/polylang.php is active.

[PRO] Improvements

  • Added automation rules:
    • Polylang: When publishing a post, translate it to all languages (Gutenberg)
    • Polylang: When publishing a post, translate it to all languages (Classic editor)
    • Polylang: When publishing a post, set the featured image for each language on all translation posts
    • Polylang: When publishing a post, set the tags and categories for each language on all translation posts

Added integration with Polylang

Gato GraphQL PRO now has an integration with the Polylang plugin.

The GraphQL schema is provided the fields to retrieve multilingual data.

Types Root/QueryRoot

Query the site metadata configured in Polylang.

Field Description
polylangDefaultLanguage Default language on Polylang, or null if there are no languages enabled.
polylangEnabledLanguages Enabled languages on Polylang.

Running this query:

{
  polylangDefaultLanguage
  polylangEnabledLanguages
}

...might produce:

{
  "data": {
    "polylangDefaultLanguage": "en",
    "polylangEnabledLanguages": [
      "en",
      "es",
      "fr"
    ]
  }
}

Types Post, Page, PostTag, PostCategory and Media

Query the language for the entity, and the IDs for the translations for that entity.

These types implement interface PolylangTranslatable. (Type Media does only when media support is enabled, via the Polylang settings.)

Field Description
polylangLanguage Language code of the post or page, or null if no language was assigned (eg: Polylang was installed later on).
polylangTranslationLanguageIDs Nodes for all the translation languages for the entity, as a JSON object with the language code as key and entity ID as value, or null if no language was assigned (eg: Polylang was installed later on).

Field polylangTranslationLanguageIDs provides the post/page IDs for all the translations. It accepts field includeSelf, to indicate if to include the queried entity's ID in the results (it's false by default).

Running this query:

{
  posts {
    __typename
    id
    title
    polylangLanguage
    polylangTranslationLanguageIDs
    polylangTranslationLanguageIDsWithSelf: polylangTranslationLanguageIDs(filter: { includeSelf: true })

    categories {
      __typename
      id
      name
      polylangLanguage
      polylangTranslationLanguageIDs
      polylangTranslationLanguageIDsWithSelf: polylangTranslationLanguageIDs(filter: { includeSelf: true })
    }
    
    tags {
      __typename
      id
      name
      polylangLanguage
      polylangTranslationLanguageIDs
      polylangTranslationLanguageIDsWithSelf: polylangTranslationLanguageIDs(filter: { includeSelf: true })
    }
  }

  pages {
    __typename
    id
    title
    polylangLanguage
    polylangTranslationLanguageIDs
    polylangTranslationLanguageIDsWithSelf: polylangTranslationLanguageIDs(filter: { includeSelf: true })
  }

  mediaItems {
    __typename
    id
    title
    polylangLanguage
    polylangTranslationLanguageIDs
    polylangTranslationLanguageIDsWithSelf: polylangTranslationLanguageIDs(filter: { includeSelf: true })
  }
}

...might produce:

{
  "data": {
    "posts": [
      {
        "__typename": "Post",
        "id": 1668,
        "title": "Some post translated using Polylang",
        "polylangLanguage": "en",
        "polylangTranslationLanguageIDs": {
          "fr": 1670,
          "es": 1672
        },
        "polylangTranslationLanguageIDsWithSelf": {
          "en": 1668,
          "fr": 1670,
          "es": 1672
        },
        "categories": [
          {
            "__typename": "PostCategory",
            "id": 61,
            "name": "Category for Polylang",
            "polylangLanguage": "en",
            "polylangTranslationLanguageIDs": {
              "fr": 63,
              "es": 65
            },
            "polylangTranslationLanguageIDsWithSelf": {
              "en": 61,
              "fr": 63,
              "es": 65
            }
          }
        ],
        "tags": [
          {
            "__typename": "PostTag",
            "id": 67,
            "name": "Tag for Polylang",
            "polylangLanguage": "en",
            "polylangTranslationLanguageIDs": {
              "fr": 69,
              "es": 71
            },
            "polylangTranslationLanguageIDsWithSelf": {
              "en": 67,
              "fr": 69,
              "es": 71
            }
          }
        ]
      }
    ],
    "pages": [
      {
        "__typename": "Page",
        "id": 1674,
        "title": "Some page translated using Polylang",
        "polylangLanguage": "en",
        "polylangTranslationLanguageIDs": {
          "fr": 1676,
          "es": 1678
        },
        "polylangTranslationLanguageIDsWithSelf": {
          "en": 1674,
          "fr": 1676,
          "es": 1678
        }
      }
    ],
    "mediaItems": [
      {
        "__typename": "Media",
        "id": 40,
        "title": "Media-for-Polylang",
        "polylangLanguage": "en",
        "polylangTranslationLanguageIDs": {
          "fr": 42,
          "es": 44
        },
        "polylangTranslationLanguageIDsWithSelf": {
          "en": 40,
          "fr": 42,
          "es": 44
        }
      }
    ]
  }
}

Types GenericCustomPost, GenericTag and GenericCategory

These types implement interface PolylangMaybeTranslatable.

GenericCustomPost is a type used to represent any custom post installed on the site, such as Portfolio, Event, Product, or other. Similarly, GenericTag and GenericCategory are used to represent their taxonomies.

Each of these CPTs and taxonomies can be defined to be translatable on the Polylang settings. Fields polylangLanguage and polylangTranslationLanguageIDs will then have the same behavior as for Post and the others (described above), and also return null if the entity's CPT or taxonomy is not configured to be translated.

In addition, field polylangIsTranslatable indicates if the CPT or taxonomy is configured to be translatable.

Field Description
polylangLanguage Language code of the post or page, or null if no language was assigned (eg: Polylang was installed later on), or if the entity is not configured to be translated (via Polylang Settings).
polylangTranslationLanguageIDs Nodes for all the translation languages for the entity, as a JSON object with the language code as key and entity ID as value, or null if no language was assigned (eg: Polylang was installed later on), or if the entity is not configured to be translated (via Polylang Settings).
polylangIsTranslatable Indicate if the entity can be translated.

Running this query:

{
  customPosts(filter: { customPostTypes: ["some-cpt", "another-cpt"] }) {
    __typename
    ...on GenericCustomPost {
      id
      title
      customPostType
      polylangIsTranslatable
      polylangLanguage
      polylangTranslationLanguageIDs
      polylangTranslationLanguageIDsWithSelf: polylangTranslationLanguageIDs(filter: { includeSelf: true })
      
      categories(taxonomy: "some-category") {
        __typename
        ...on GenericCategory {
          id
          name
          polylangIsTranslatable
          polylangLanguage
          polylangTranslationLanguageIDs
          polylangTranslationLanguageIDsWithSelf: polylangTranslationLanguageIDs(filter: { includeSelf: true })
        }
      }
      
      tags(taxonomy: "some-tag") {
        __typename
        ...on GenericTag {
          id
          name
          polylangIsTranslatable
          polylangLanguage
          polylangTranslationLanguageIDs
          polylangTranslationLanguageIDsWithSelf: polylangTranslationLanguageIDs(filter: { includeSelf: true })
        }
      }
    }
  }
}

...might produce:

{
  "data": {
    "customPosts": [
      {
        "__typename": "GenericCustomPost",
        "id": 10,
        "title": "Some CPT that has Polylang translation enabled",
        "customPostType": "some-cpt",
        "polylangIsTranslatable": true,
        "polylangLanguage": "en",
        "polylangTranslationLanguageIDs": {
          "fr": 12,
          "es": 14
        },
        "polylangTranslationLanguageIDsWithSelf": {
          "en": 10,
          "fr": 12,
          "es": 14
        },
        "categories": [
          {
            "__typename": "GenericCategory",
            "id": 30,
            "name": "Some Category for Polylang",
            "polylangIsTranslatable": true,
            "polylangLanguage": "en",
            "polylangTranslationLanguageIDs": {
              "fr": 32,
              "es": 34
            },
            "polylangTranslationLanguageIDsWithSelf": {
              "en": 30,
              "fr": 32,
              "es": 34
            }
          }
        ],
        "tags": [
          {
            "__typename": "GenericTag",
            "id": 50,
            "name": "Some Tag for Polylang",
            "polylangIsTranslatable": true,
            "polylangLanguage": "en",
            "polylangTranslationLanguageIDs": {
              "fr": 52,
              "es": 54
            },
            "polylangTranslationLanguageIDsWithSelf": {
              "en": 50,
              "fr": 52,
              "es": 54
            }
          }
        ]
      },
      {
        "__typename": "GenericCustomPost",
        "id": 20,
        "title": "Another CPT that does not have Polylang translation enabled",
        "customPostType": "another-cpt",
        "polylangIsTranslatable": false,
        "polylangLanguage": null,
        "polylangTranslationLanguageIDs": null,
        "polylangTranslationLanguageIDsWithSelf": null,
        "categories": [
          {
            "__typename": "GenericCategory",
            "id": 70,
            "name": "Category without support for Polylang",
            "polylangIsTranslatable": false,
            "polylangLanguage": null,
            "polylangTranslationLanguageIDs": null,
            "polylangTranslationLanguageIDsWithSelf": null
          }
        ],
        "tags": [
          {
            "__typename": "GenericTag",
            "id": 72,
            "name": "Tag without support for Polylang",
            "polylangIsTranslatable": false,
            "polylangLanguage": null,
            "polylangTranslationLanguageIDs": null,
            "polylangTranslationLanguageIDsWithSelf": null
          }
        ]
      }
    ]
  }
}

2.2.3

3 weeks ago

Fixed

  • Bug parsing @export(as: $someVar) (#2661)

2.2.2

1 month ago

Improvements

  • Adapted blocks field to work with WordPress 6.5 (#2657)
  • Tested up WordPress 6.5
  • Renamed "Tutorial" to "Schema tutorial"

2.2.1

2 months ago

Improvements

  • Added "Lesson (number): " in the tutorials

2.2.0

2 months ago

Improvements

Do not include bundles in the Extensions page

In order to simplify the product offering, Gato GraphQL PRO will now be released as a single plugin, containing all the extensions. As such, installing bundles and single extensions are now both deprecated, and not offered anymore.

Do not print the required extensions on predefined persisted queries

Similar to above, predefined persisted queries are now simplified. These now have "[PRO]" prepended to their title, and the list of required extensions (printed on the body of the query) is removed.

2.1.3

3 months ago

Improvements

  • Added documentation for fields _arrayFlipToObject and _objectIntersectKey from the PHP Functions via Schema extension
  • Added documentation for field _arrayOfJSONObjectsExtractProperty from the Helper Function Collection extension

2.1.0

3 months ago

Release Notes: 2.1

Added

Support providing the Schema Configuration to apply when invoking the Internal GraphQL Server

Gato GraphQL now supports providing the Schema Configuration to apply when executing a query via an internal GraphQL Server (i.e. directly within the PHP application, not via an endpoint).

This new feature enhances several PRO extensions.

The Internal GraphQL Server extension makes use of this feature. It now accepts a $schemaConfigurationIDOrSlug parameter on methods executeQuery and executeQueryInFile from the GraphQLServer class, and already extracts the schema configuration used by the persisted query on executePersistedQuery:

class GraphQLServer {
  
  public static function executeQuery(
    string $query,
    array $variables = [],
    ?string $operationName = null,
+   // Accept parameter 
+   int|string|null $schemaConfigurationIDOrSlug = null,
  ): Response {
    // ...
  }

  public static function executeQueryInFile(
    string $file,
    array $variables = [],
    ?string $operationName = null,
+   // Accept parameter 
+   int|string|null $schemaConfigurationIDOrSlug = null,
  ): Response {
    // ...
  }

+ // Schema Configuration is taken from the Persisted Query
  public static function executePersistedQuery(
    string|int $persistedQueryIDOrSlug,
    array $variables = [],
    ?string $operationName = null,
  ): Response {
    // ...
  }

The Automation extension also benefits from this new feature.

It now provides a user interface for its "automator" functionality (directly via the WordPress editor), called Automation Configurator. The automation trigger is any WordPress action hook, and the action is the execution of a GraphQL persisted query.

Automation Rule editor

For instance, when creating a new post, automation rule Add comments block to new post checks if the core/comments block is present and, if not, it adds it at the bottom of the post:

Automatically inserting the comments block to new 'draft' posts

Predefined persisted query "Insert block in post"

The newly-added persisted GraphQL query "Insert block in post" allows to inject a block in a post. It identifies the nth block of a given type (wp:paragraph by default) in a post, and places the provided custom block's HTML content right after it.

Used with the Automation extension, this persisted query can be used to automatically inject mandatory blocks to a newly-published post (eg: a CTA block to promote an ongoing campaign).

Improvements

  • If initializing the service container from the cache fails, fallback to initializing PHP object from memory (#2638)
  • Give unique operationName to all predefined persisted queries (#2644)
  • Improved error message when fetching blocks from a post, and the block content has errors
  • Completed documentation for the Automation extension (#2651)
  • On the "Generate a post's featured image using AI and optimize it" predefined persisted query, execute logic only if the post title is not empty (#ec931dd)

Fixed

  • Bug in multi-control JS component used by extensions (Access Control, Cache Control, and Field Deprecation) showing "undefined" on the block on the Schema Configuration (#2639)
  • Bug in regex replacements in predefined persisted queries (#2649)
  • Avoid reinstalling plugin setup data if deactivating/reactivating the plugin (#2641)
  • Handle error from passing WP_Post as GraphQL variable to the Internal GraphQL Server (#2652)

2.0.1

3 months ago

These are all the changes for v2.0.

Added

Module "Media Mutations"

The new module "Media Mutations" adds fields to the schema that upload media attachments, and query for the logged-in user's media attachments.

Mutation createMediaItem

Mutation createMediaItem allows uploading files to the Media Library. It offers 2 ways to provide the source file:

  1. Via URL
  2. Directly its contents

Running this query:

mutation CreateMediaItems {
  fromURL: createMediaItem(input: {
    from: {
      url: {
        source: "https://gatographql.com/assets/GatoGraphQL-logo.png"
      }
    }
    caption: "Gato GraphQL logo"
    altText: "This is the Gato GraphQL logo"
  }) {
    mediaItemID
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    mediaItem {
      ...MediaItemData
    }
  }

  directlyByContents: createMediaItem(input: {
    from: {
      contents: {
        body: """
<html>
  <body>
    Hello world!
  </body>
</html>
        """
        filename: "hello-world.html"
      }
    }
    title: "Hello world!"
  }) {
    mediaItemID
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    mediaItem {
      ...MediaItemData
    }
  }
}

fragment MediaItemData on Media {
  altText
  caption
  mimeType
  slug
  src
  title
}

...will produce:

{
  "data": {
    "fromURL": {
      "mediaItemID": 1380,
      "status": "SUCCESS",
      "errors": null,
      "mediaItem": {
        "altText": "This is the Gato GraphQL logo",
        "caption": "Gato GraphQL logo",
        "mimeType": "image/png",
        "slug": "gatographql-logo-png",
        "src": "https://mysite.com/wp-content/uploads/GatoGraphQL-logo.png",
        "title": "GatoGraphQL-logo.png"
      }
    },
    "directlyByContents": {
      "mediaItemID": 1381,
      "status": "SUCCESS",
      "errors": null,
      "mediaItem": {
        "altText": "",
        "caption": "",
        "mimeType": "text/html",
        "slug": "hello-world-html",
        "src": "https://mysite.com/wp-content/uploads/hello-world.html",
        "title": "Hello world!"
      }
    }
  }
}

Fields myMediaItemCount, myMediaItems and myMediaItem

Logged-in users can now retrieve all of their media files.

Running this query:

query GetMediaItems {
  me {
    slug
  }
  
  myMediaItemCount

  myMediaItems(pagination: {
    limit: 3
  }) {
    ...MediaItemData
  }

  myMediaItem(by: { id: 1380 }) {
    ...MediaItemData
  }
}

fragment MediaItemData on Media {
  id
  mimeType
  src
  author {
    slug
  }
}

...will produce:

{
  "data": {
    "me": {
      "slug": "admin"
    },
    "myMediaItemCount": 2,
    "myMediaItems": [
      {
        "id": 1380,
        "mimeType": "image/png",
        "src": "https://mysite.com/wp-content/uploads/GatoGraphQL-logo.png",
        "author": {
          "slug": "admin"
        }
      },
      {
        "id": 1365,
        "mimeType": "image/png",
        "src": "https://mysite.com/wp-content/uploads/browser.png",
        "author": {
          "slug": "admin"
        }
      }
    ],
    "myMediaItem": {
      "id": 1380,
      "mimeType": "image/png",
      "src": "https://mysite.com/wp-content/uploads/GatoGraphQL-logo.png",
      "author": {
        "slug": "admin"
      }
    }
  }
}

A new predefined Persisted query, with title "Generate a post's featured image using AI and optimize it", has been added.

It uses generative AI to produce images for posts without a featured image, choosing from these service providers:

It first checks if a post has a featured image. If it does not, it creates one by calling the generative AI service. We must provide the corresponding API key for the chosen service to use.

As the generative AI images are not optimized for the web, the query can also send the newly-generated image to TinyPNG to compress it. We must provide the API key to use it.

Finally, the query creates a new media item with the image, and sets it as the post's featured image.

Documentation for new field _dataMatrixOutputAsCSV from the Helper Function Collection extension

Field _dataMatrixOutputAsCSV has been added to the documentation for the Helper Function Collection extension.

This field takes a matrix of data, and produces a CSV string. For instance, this query:

csv: _dataMatrixOutputAsCSV(
  fields: 
    ["Name", "Surname", "Year"]
  data: [
    ["John", "Smith", 2003],
    ["Pedro", "Gonzales", 2012],
    ["Manuel", "Perez", 2008],
    ["Jose", "Pereyra", 1999],
    ["Jacinto", "Bloomberg", 1998],
    ["Jun-E", "Song", 1983],
    ["Juan David", "Santamaria", 1943],
    ["Luis Miguel", null, 1966],
  ]
)

...will produce:

{
  "data": {
    "csv": "Name,Surname,Year\nJohn,Smith,2003\nPedro,Gonzales,2012\nManuel,Perez,2008\nJose,Pereyra,1999\nJacinto,Bloomberg,1998\nJun-E,Song,1983\nJuan David,Santamaria,1943\nLuis Miguel,,1966\n"
  }
}

Improvements

  • Validate the license keys when updating the plugin
  • Simplified the Tutorial section
  • Prevent max execution time issues when installing plugin on (cheap) shared hosting (#2631)
    • Validate that the PHP memory limit is at least 64MB to load Gato GraphQL
    • Store the new plugin versions to DB only after generating the service container cache
    • Disable the max execution time when compiling the container

Fixed

  • Bug where a syntax error on a variable definition in the GraphQL query was not validated

Breaking changes

Field resolver's validateFieldArgValue method receives extra argument $fieldArgs

It is now possible for a field to validate an input based on the value of another input.

For this, method validateFieldArgValue from AbstractObjectTypeFieldResolver now receives a $fieldArgs param, which contains the values for all inputs provided to the field:

/**
 * Validate the constraints for a field argument
 * @param array<string,mixed> $fieldArgs
 */
public function validateFieldArgValue(
    ObjectTypeResolverInterface $objectTypeResolver,
    string $fieldName,
    string $fieldArgName,
    mixed $fieldArgValue,
    AstInterface $astNode,
    array $fieldArgs,
    ObjectTypeFieldResolutionFeedbackStore $objectTypeFieldResolutionFeedbackStore,
): void;

1.6.0

3 months ago

Added

Module "Media Mutations"

The new module "Media Mutations" adds fields to the schema that upload media attachments, and query for the logged-in user's media attachments.

Mutation createMediaItem

Mutation createMediaItem allows uploading files to the Media Library. It offers 2 ways to provide the source file:

  1. Via URL
  2. Directly its contents

Running this query:

mutation CreateMediaItems {
  fromURL: createMediaItem(input: {
    from: {
      url: {
        source: "https://gatographql.com/assets/GatoGraphQL-logo.png"
      }
    }
    caption: "Gato GraphQL logo"
    altText: "This is the Gato GraphQL logo"
  }) {
    mediaItemID
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    mediaItem {
      ...MediaItemData
    }
  }

  directlyByContents: createMediaItem(input: {
    from: {
      contents: {
        body: """
<html>
  <body>
    Hello world!
  </body>
</html>
        """
        filename: "hello-world.html"
      }
    }
    title: "Hello world!"
  }) {
    mediaItemID
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    mediaItem {
      ...MediaItemData
    }
  }
}

fragment MediaItemData on Media {
  altText
  caption
  mimeType
  slug
  src
  title
}

...will produce:

{
  "data": {
    "fromURL": {
      "mediaItemID": 1380,
      "status": "SUCCESS",
      "errors": null,
      "mediaItem": {
        "altText": "This is the Gato GraphQL logo",
        "caption": "Gato GraphQL logo",
        "mimeType": "image/png",
        "slug": "gatographql-logo-png",
        "src": "https://mysite.com/wp-content/uploads/GatoGraphQL-logo.png",
        "title": "GatoGraphQL-logo.png"
      }
    },
    "directlyByContents": {
      "mediaItemID": 1381,
      "status": "SUCCESS",
      "errors": null,
      "mediaItem": {
        "altText": "",
        "caption": "",
        "mimeType": "text/html",
        "slug": "hello-world-html",
        "src": "https://mysite.com/wp-content/uploads/hello-world.html",
        "title": "Hello world!"
      }
    }
  }
}

Fields myMediaItemCount, myMediaItems and myMediaItem

Logged-in users can now retrieve all of their media files.

Running this query:

query GetMediaItems {
  me {
    slug
  }
  
  myMediaItemCount

  myMediaItems(pagination: {
    limit: 3
  }) {
    ...MediaItemData
  }

  myMediaItem(by: { id: 1380 }) {
    ...MediaItemData
  }
}

fragment MediaItemData on Media {
  id
  mimeType
  src
  author {
    slug
  }
}

...will produce:

{
  "data": {
    "me": {
      "slug": "admin"
    },
    "myMediaItemCount": 2,
    "myMediaItems": [
      {
        "id": 1380,
        "mimeType": "image/png",
        "src": "https://mysite.com/wp-content/uploads/GatoGraphQL-logo.png",
        "author": {
          "slug": "admin"
        }
      },
      {
        "id": 1365,
        "mimeType": "image/png",
        "src": "https://mysite.com/wp-content/uploads/browser.png",
        "author": {
          "slug": "admin"
        }
      }
    ],
    "myMediaItem": {
      "id": 1380,
      "mimeType": "image/png",
      "src": "https://mysite.com/wp-content/uploads/GatoGraphQL-logo.png",
      "author": {
        "slug": "admin"
      }
    }
  }
}

A new predefined Persisted query, with title "Generate a post's featured image using AI and optimize it", has been added.

It uses generative AI to produce images for posts without a featured image, choosing from these service providers:

It first checks if a post has a featured image. If it does not, it creates one by calling the generative AI service. We must provide the corresponding API key for the chosen service to use.

As the generative AI images are not optimized for the web, the query can also send the newly-generated image to TinyPNG to compress it. We must provide the API key to use it.

Finally, the query creates a new media item with the image, and sets it as the post's featured image.

Documentation for new field _dataMatrixOutputAsCSV from the Helper Function Collection extension

Field _dataMatrixOutputAsCSV has been added to the documentation for the Helper Function Collection extension.

This field takes a matrix of data, and produces a CSV string. For instance, this query:

csv: _dataMatrixOutputAsCSV(
  fields: 
    ["Name", "Surname", "Year"]
  data: [
    ["John", "Smith", 2003],
    ["Pedro", "Gonzales", 2012],
    ["Manuel", "Perez", 2008],
    ["Jose", "Pereyra", 1999],
    ["Jacinto", "Bloomberg", 1998],
    ["Jun-E", "Song", 1983],
    ["Juan David", "Santamaria", 1943],
    ["Luis Miguel", null, 1966],
  ]
)

...will produce:

{
  "data": {
    "csv": "Name,Surname,Year\nJohn,Smith,2003\nPedro,Gonzales,2012\nManuel,Perez,2008\nJose,Pereyra,1999\nJacinto,Bloomberg,1998\nJun-E,Song,1983\nJuan David,Santamaria,1943\nLuis Miguel,,1966\n"
  }
}

Improvements

  • Validate the license keys when updating the plugin
  • Simplified the Tutorial section
  • Prevent max execution time issues when installing plugin on (cheap) shared hosting (#2631)
    • Validate that the PHP memory limit is at least 64MB to load Gato GraphQL
    • Store the new plugin versions to DB only after generating the service container cache
    • Disable the max execution time when compiling the container

Fixed

  • Bug where a syntax error on a variable definition in the GraphQL query was not validated

1.5.4

4 months ago

Fixed

  • Bug in resolver where innerBlocks is not set