TheoretiCAL
TheoretiCAL

Reputation: 20581

Creating page with custom url / meta tags using Wordpress REST API

I'm trying to create a custom page on my domain that has the url 'http://example.com/test/slug', however the slug does not seem to support slashes (the slug '/test/slug' turns into '/testslug'). Is it possible using the rest api to create a page on a custom url? Also, I populate the 'meta' dictionary, however the page created does not contain a meta 'description' tag in the header. How can I correctly create custom meta tags?

import requests

url = 'http://example.com/wp-json/wp/v2/pages'
data = {'content': '<h2>test content</h2>',
 'meta': {'description': 'this is a test meta field'},
 'slug': 'test/slug',
 'status': 'publish',
 'title': 'test title'}

resp = requests.post(url, json=data, auth=('user','pass'), headers={'Content-Type':'application/json'})

Upvotes: 4

Views: 4502

Answers (3)

Lucas Bustamante
Lucas Bustamante

Reputation: 17297

First, make sure your Permalinks settings is "Post Name".

This can be configured under http://<yoursite.com>/wp-admin/options-permalink.php

I have studied "Custom Permalinks" plugin code to understand how to answer this question, it saves a metadata called "custom_permalink" and does heavy INNER JOINS to make WordPress core work with "fake" sub-slugs.

I came up with a different solution. The trick here is to create a fake parent page to serve as a base URL for children pages. I have written it in PHP, since I don't know Python.

<?php

require_once('helpers.php');

define('API_URL', 'http://temp.localhost');

# Let's get all pages in an array
$pages = curlGet(API_URL.'/wp-json/wp/v2/pages');

# Your usual $args
$args = array(
    'title' => 'API TEST',
    'status' => 'draft',
    'content' => 'content',
    'slug' => 'some/thing'
);

# We intercept it here, before sending to WordPress
$args = maybe_add_parent($args);

$response = curlPost( API_URL.'/wp-json/wp/v2/pages/', $args );

/**
*   Receives an $args array that would be sent to WordPress API and checks if we need to add a parent page
*/
function maybe_add_parent(array $args) {
    if (array_key_exists('slug', $args)) {
        # Has parent?
        if (strpos($args['slug'], '/') !== false) {
            $parent = explode('/', $args['slug']);
            # For simplicity sake let's do it parent/chidren slugs only
            if (count($parent) !== 2) {
                die('This script can only run parent/children slugs');
            }
            $slug = array_pop($parent);

            # Here, we will check the parent to see if it exists.
            $parent_id = slug_exists($parent[0]);
            if ($parent_id === false) {
                # If it does not, it will create it and return it's ID
                $parent_id = create_parent($parent[0]);
            }
            # Add parent ID to $args.
            $args['parent'] = $parent_id;
            # Rename the slug
            $args['slug'] = $slug;
        }
    }
    return $args;
}

/**
*   Checks if a given slug exists in $pages
*/
function slug_exists(string $slug) {
    global $pages;
    foreach ($pages as $page) {
        # Checks if a "Parent" page with this slug exists
        if ($page['slug'] == $slug && $page['parent'] == 0) {
            return true;
        }
    }
    return false;
}

/**
*   Creates a parent page
*/
function create_parent(string $slug) {
    $args = array(
        'title' => $slug,
        'status' => 'draft',
        'content' => '',
        'slug' => $slug
    );
    $response = json_decode(curlPost( API_URL.'/wp-json/wp/v2/pages/', $args ));
    return $response->id;
}

The script does the following:

  • Send a GET request for all the pages on the website.
  • Intercepts an $args array that would be sent to the WordPress API.
  • Checks if this $args contains a slug in the format parent/child
  • If it does, check if parent page exists
  • If it doesn't, it will create this page as a draft and return it's ID
  • It the parent already exists, it will just return it's ID
  • With the Parent ID, it will modify $args adding parent key and return $args
  • When WordPress sees that that page has a parent, it will automatically add it's slug in the url, as in parent/child

It works:

slug api wordpress create page

Upvotes: 2

Sally CJ
Sally CJ

Reputation: 15620

the slug does not seem to support slashes (the slug '/test/slug' turns into '/testslug').

Yes, because whether you use the REST API or the admin UI for creating Pages, the slug is limited to alphanumeric characters, plus dashes (-) and underscores (_). See https://developer.wordpress.org/rest-api/reference/pages/#schema-slug.

You can remove the limitation by adding remove_filter( 'sanitize_title', 'sanitize_title_with_dashes', 10, 3 ); to the theme's functions.php file (or a custom plugin); however, visiting the page (URL) would by default throw a 404 (not found) error. There are ways to fix that, but in short, you should not remove the filter.

Is it possible using the rest api to create a page on a custom url?

No it's not, not by default.

However, if you want http://example.com/test/slug to serve/show a Page/Post/etc. be it created via the REST API or not, then you can use custom URL rewrite rules, e.g. via add_rewrite_rule().

the page created does not contain a meta 'description' tag in the header. How can I correctly create custom meta tags?

You need to register the meta key, which in your case is description.

See

And you can register it using the register_meta() function in wp-includes/meta.php.

Example for your description meta:

<?php
// The object type. For custom post types, this is 'post';
// for custom comment types, this is 'comment'. For user meta,
// this is 'user'.
$object_type = 'post'; // 'post' even for Pages
$args1 = array( // Validate and sanitize the meta value.
    // Note: currently (4.7) one of 'string', 'boolean', 'integer',
    // 'number' must be used as 'type'. The default is 'string'.
    'type'         => 'string',
    // Shown in the schema for the meta key.
    'description'  => 'A meta key associated with a string meta value.',
    // Return a single value of the type.
    'single'       => true,
    // Show in the WP REST API response. Default: false.
    'show_in_rest' => true,
);
register_meta( $object_type, 'description', $args1 );

To quickly test if the meta description was successfully registered and that it's being available from the REST API, perform a GET request to http://example.com/wp-json/wp/v2/pages/<id> where <id> is the Page ID.

Upvotes: 2

Cameron Hurd
Cameron Hurd

Reputation: 5041

To get the URL structure you want, the most straightforward way is to create a child page that references an already existing page as it's parent.

To amend your example above:

import requests

url = 'http://example.com/wp-json/wp/v2/pages'
parent_page = 43 # assuming that page with URL '/test' has id of 42
data = {'content': '<h2>test content</h2>',
 'meta': {'description': 'this is a test meta field'},
 'slug': 'slug',
 'status': 'publish',
 'title': 'test title'
 'parent': parent_page} # <--

resp = requests.post(url, json=data, auth=('user','pass'), headers={'Content-Type':'application/json'})

Would give you a new page at example.com/test/slug

Reference to arguments for "Create a Page", (POST /wp/v2/pages) on WordPress rest API Handbook: https://developer.wordpress.org/rest-api/reference/pages/#create-a-page

Upvotes: 1

Related Questions