• Resolved nr2012

    (@nr2012)


    Hi there

    I am using wordpress’s REST API to get the data.
    For now the «preview Changes» does not have an effect at all, but just links to the old (frontend) page without the changes.

    What I would need is calling a different frontend template (Vue.js) which get’s the data directly from the REST API.
    I found this post: https://wordpress.org/support/topic/preview-changes-in-rest-api/
    And I understand that the preview is a revision of the page/post I am calling.
    I also understand that I would expect the changed content by fetching a url like this:
    https://DOMAIN.com/api/wp/v2/pages/51/revisions
    But if I do that I get this response:

    {"code":"rest_cannot_read","message":"Sorry, you are not allowed to view revisions of this post.","data":{"status":401}}

    This is due to the fact, that something with the authentication does not work properly. I thought I might find an answer here:
    https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/

    But there it says:

    For developers using the built-in Javascript API, this is handled automatically for you. This is the recommended way to use the API for plugins and themes. Custom data models can extend wp.api.models.Base to ensure this is sent correctly for any custom requests.

    Which I don’t fully understand, but «handled automatically for me» would let me think, that if I’m logged in in the backend, a cookie would let me see the restricted revisions content within the API. Obviously this is not the case.

    And if the authentication problem is solved I am still unsure what to do next.
    If I call the page with the «Preview Changes» button, I end up calling an url like
    https://domain.com/pagename/?preview_id=51&preview_nonce=c83809a022&preview=true

    Does this mean I would have to rewrite the vue template to execute a different call to fetch the data, if the URLs GET params contain preview=true

    Hope someone can help me out here.
    Thanks a lot and
    cheers

    J

    — Edit:

    I just tried passing the _wpnonce via GET paramater:

    /api/wp/v2/pages/51/?_wpnonce=c83809a022

    I edited the page within the backend and hit the preview button. This led me to my frontend (with the old content) but with an url .../?preview_id=51&preview_nonce=c83809a022&preview=true.
    This is where I copied the nonce and added it to the url where I was calling the revisions directly.

    So the nonce should be quite new, but no I get this message:

    {"code":"rest_cookie_invalid_nonce","message":"Cookie nonce is invalid","data":{"status":403}}

    • This topic was modified 7 years, 5 months ago by nr2012.
    • This topic was modified 7 years, 5 months ago by nr2012.
Viewing 9 replies - 1 through 9 (of 9 total)
  • Thread Starter nr2012

    (@nr2012)

    I made some further progress:

    I created a new nonce when clicking on the preview changes button:

    function custom_preview_page_link($link) {
    	$id = get_the_ID();
    	$nonce = wp_create_nonce( 'wp_rest' );
    	$link = 'http://mydomain.dev/api/wp/v2/pages/'. $id. '/revisions/?_wpnonce='. $nonce;
    	return $link;
    }
    add_filter('preview_post_link', 'custom_preview_page_link');

    This would actually bring me to my REST API and show me the right page with the current changes. So I got the authentication to work.

    A further problem is that I use a lot of ACFs which are not core of WP. So I also use a plugin called
    ACT-TO-REST-API. And now my problem just move a bit, because the revisions would not include the (changed) ACF fields.

    I know that this problem is shifting the problem away from the original post title, but maybe someone still feels like helping me out :).

    Cheers

    • This reply was modified 7 years, 5 months ago by nr2012.

    Hi nr2012,

    Thanks for the custom preview page link function. I tweaked that a little so it would direct the users to a /preview/ page with the the necessary post type, id, and nonce so I could grab that information in my vue component so I could render the correct information with the correct template and styles for the user.

    In case you still need help accessing your acf values, try adding this code to your functions.php file.

    // add acf values to revisions rest api
    add_filter( 'rest_prepare_revision', function( $response, $post ) {
        $data = $response->get_data();
        $data['acf'] = get_fields( $post->ID );
    
        return rest_ensure_response( $data );
    }, 10, 2 );

    Happy coding!

    Thread Starter nr2012

    (@nr2012)

    Hi @maggiesadler

    I got it all sorted as well.
    https://github.com/airesvsg/acf-to-rest-api/issues/190
    airesvsg answered with something like you are proposing. Maybe you even got that information from there.. :).

    Well thanks anyway.
    Cheers

    mmeridyth

    (@mmeridyth)

    Hi @nr2012

    Thanks for writing all your thought process here, really helpful. Do you mind if I ask if your WP site and your Vue application were on the same codebase?

    For instance my wordpress site is at content.domain.local but my vue application is on another url domain.local and im having issues authenticating because my functions.php file is not in the same codebase and all the examples like this won’t work

    wp_localize_script( 'my-script', 'MyScript', array(
    	'nonce' => wp_create_nonce( 'wp_rest' ),
    ) );
    jQuery.ajax( {
    	url: url,
    	data: data,
    	beforeSend: function( request ) {
    		request.setRequestHeader( 'X-WP-Nonce', MyScript.nonce );
    	},
    } )

    Did you try to get the draft preview working too or just the preview changes button?

    Cheers

    Thread Starter nr2012

    (@nr2012)

    @mmeridyth
    Hey, no we also have them running completely separately.
    I don’t really understand exactly what you are asking. Your functions.php is not in the wordpress codebase?
    Maybe you could elaborate this a bit. Your backend has to provide a nonce and with this you should be able to access the API via your vue application.

    I indeed did get the preview for drafts to work as well.
    Just tell me what kind of help you need. I did this a few months ago, so I don’t have everything present anymore.

    Cheers

    mmeridyth

    (@mmeridyth)

    @nr2012
    Its good to know that I can get it working with separate code bases.
    I want to know how you do an API call from your Vue application and if you always have to have the nonce in the url if its a draft or preview you are trying to see?

    For instance the call to see one of my projects is – http://content.domain.local/wp-json/wp/v2/projects/?slug=project-one

    I’ve made a custom endpoint for this custom post type with all the fields I would like but because it’s a draft post I get a permissions error. It works fine when the project is published. But I want to be able to preview projects before I publish them.

    In my Vue application from the examples I’ve seen I need to have a header set to see restricted content –

    let url = store.state.apiUrl + 'projects/?slug='+params.slug
    axios.get(url, { 
      headers: { 
        'X-WP-Nonce': 'how to get this' 
      } 
    })
    .then(response => {
      // If request is good...
      console.log(response.data);
    })
    .catch((error) => {
      console.log('error ' + error);
    });

    I’ve been trying to understand the authentication documentation here https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/ but I don’t understand how to do it when I can’t get the nonce value for the header because the examples are getting it from a wp_localize_script. When im in my Vue application I don’t have access to anything in my functions.php file (different codebase). So is the only way to authenticate the API call by having in nonce in the API url like your example for previewing changes or is there another way?

    Thread Starter nr2012

    (@nr2012)

    Ah well, so as far as I understand you don’t know to communicate the nonce between API and frontend, right?

    I did something like this:

    I send nonce and ID of the page/post in the url. In the frontend I check for the router to get the queries.

    // Save query strings
        const wpnonce = rootState.route.query.preview_nonce
        const previewId = rootState.route.query.preview_id

    I build my preview URL:
    const previewUrl =/api/previews/v1/preview/?id=${previewId}&_wpnonce=${wpnonce}

    which is of course different for your endpoint.

    We then make an axios call to get the data:

    export function getPreviewData (backendUrl, previewUrl) {
      return axios
        .get(<code>${backendUrl + previewUrl}</code>, {
          withCredentials: true
        })
        .then(res => res.data)
    }

    As you see you of course have to define your backendurl to concat the right string, then make sure to set the withCredentials to true.

    And of course as said above you have to create a nonce and then change the link which is called by the preview button:

    Here is the link creation code:

    
    function custom_preview_page_link( $link ) {
    	$id = get_the_ID();
    	// For authentication a nonce must be created
    	// otherwise the REST API does not allow users to see previews
    	$nonce = wp_create_nonce( 'wp_rest' );
    	$post = get_post( $id );
    	$slug = $post->post_name;
    	$lang = '';
    	$isPost = '';
    
    	// if we preview a post, we want to add 'posts/' to the url to link to our posts site in the frontend
    	// TODO this URL schould be dynamicly the same for back- and frontend
    	if ($post->post_type === 'post') {
    		$isPost = '';
    	}
    
    	// if we have a new page without a slug we show /preview/ as a page
    	if ($slug === '') {
    		$slug = 'preview';
    	}
    
    	// Get the language of the polylang custom field
    	// https://polylang.wordpress.com/documentation/documentation-for-developers/functions-reference/
    	$current_lang = pll_get_post_language( $id );
    	$default_lang = pll_default_language( 'slug' );
    
    	// Only add a language if it's not the default language
    	if ($current_lang !== $default_lang) {
    		$lang = $current_lang . '/';
    	}
    
    	/* make sure WP_HOME (wp-config.php) always has exactly one trailing slash.
    	Remove if it has multiple then add one or add one if it has no slash */
    	$home_path = rtrim(WP_HOME, '/') . '/';
    	$link = $home_path . $lang . $isPost . $slug . '/?preview_id='. $id. '&preview_nonce=' . $nonce . '&preview=true';
    	return $link;
    }
    add_filter('preview_post_link', 'custom_preview_page_link');
    

    If you preview an unpublished post we don’t have a slug.
    What we obviously need to build, which I just realized 😀 – is that we take different templates to show the preview if it is a post or a page, because we have different vue templates for those.

    I have not fully dived into the code, but I hope this helps. Otherwise keep asking.

    BTW: two generated Urls on click on the preview button:
    Published post with changes:
    https://www.domain.org/slug/?preview_id=382&preview_nonce=79cde05647&preview=true

    And a draft (new post):
    https://www.domain.org/preview/?preview_id=411&preview_nonce=79cde05647&preview=true

    • This reply was modified 7 years ago by nr2012.
    mmeridyth

    (@mmeridyth)

    That’s great, getting the nonce from the API to the frontend is exactly what I was trying to ask, thanks for your help!

    Thread Starter nr2012

    (@nr2012)

    Alright. No worries.
    If you have any insights on your side, feel free to share. Our solution is probably still not the best.
    I am just about to work on the post preview solution, where I need to get the permalink structure of the backend which should help me choosing the right template in the frontend.

Viewing 9 replies - 1 through 9 (of 9 total)
  • The topic ‘Preview Changes with WordPress REST API (Authentication problem)’ is closed to new replies.