How to Render Password and then Protected Posts

Hey there,

I’m new to Frontity, so apologies if this is obvious. I tried searching in both the community and on Google for a potential solution first, but to no avail. I’m curious to know if there’s supported package to render the same password protection features on Wordpress.com to a Frontity project. As a reference this is the UX for password protected pages and posts on wordpress:

giphy
(yes, I’m using my wedding as an excuse to learn about Frontity :smile:)

Thanks for all the amazing work.

1 Like

I’m going to look into making my own package for this. I’m going to reference the wp-source package as a start. I’ll post updates here.

So, I was able to add a simple block in the post of the Mars Theme to give the user an interface to type in a password. This is the snippet:

// packages/mars-theme/src/components/post.js LN:58
{ post.content.protected && !post.content.rendered && (
  <div className="protected">
    <p>
      This is a protected post.
    </p>
    <form onSubmit={ submitPassword }>
      <input type="password" ref={ input } />
      <input type="submit" value="Submit" />
    </form>
  </div>
) }

And, I was able to see that you simply add ?password=PASSWORD to the Wordpress REST API in order to get the JSON populated with the password protected post/page content.

But, I don’t understand how actions work from the @frontity/tiny-router. In the submitPassword handler if I try to force an action on the same link with the added query parameters the information is not updated.

function submitPassword(e) {
  e.preventDefault();
  const uri = `${state.router.link}?password=${input.current.value}`;
  actions.source.fetch(uri, { force: true }).then(() => {
    var data = state.source.get(state.router.link);
    var post = state.source[data.type][data.id];
    // Unfortunately the post isn't populated with new data...
  });
}

My intention was to cause either a page refresh or a react flush once I knew the information was correct. Is this how actions should be used? Or should I be overriding the autoFetch from the router?

Hi @jonobr1

Firstly, welcome to the community, and secondly congratulations on the upcoming wedding. :tada: :clinking_glasses: :champagne:

You’ve made great progress on your issue so far, well done. To fetch the protected data you need to use useEffect and to populate the state with data that Frontity thinks has already been fetched you need to use { force: true } with the fetch call.

See our docs here for more detailed info on this. The last two paragraphs and code samples in that section should give you what you need.

Hope this helps, and do let us know how you get on with it.

1 Like

Thanks! Excited to be using it as an opportunity to learn Frontity.

I redid my snippet to make sure the fetch was called within a useEffect. While this code runs and the promise on the fetch call resolves, the data does not seem to be updated. The console.log("Data changed"); only fires on page load, not when the fetch call resolves.

const [refetch, setRefetch] = useState(false);

useEffect(() => {
  if (refetch) {
    console.log('refetching!');
    const uri = `${state.router.link}?password=${input.current.value}`;
    actions.source.fetch(uri, { force: true }).then(() => {
      console.log('Fetch completed');
    });
    setRefetch(false);
  }
}, [refetch]);

useEffect(() => {
  console.log('Data changed');
}, [data]);

function submitPassword(e) {
  e.preventDefault();
  setRefetch(true);
}

Is there another way I should pass query arguments to a fetch call instead of just appending them to the router link?

Okay, so I was able to get this working with a few modifications to wp-source. Here are my updates:

/**
 * packages/mars-theme/src/components/post.js L:30
 */
const input = useRef();

function submitPassword(e) {
  e.preventDefault();
  data.query.password = input.current.value;
  actions.source.fetch(`${state.router.link}?password=${input.current.value}`, { force: true });
}

/**
 * LN:67
 */
{ post.content.protected && !post.content.rendered && (
  <div className="protected">
    <p>
      This is a protected post.
    </p>
    <form onSubmit={ submitPassword }>
      <input type="password" ref={ input } />
      <input type="submit" value="Submit" />
    </form>
  </div>
) }

Then here are the changes I made to wp-source:

/**
 * @frontity/wp-source/src/actions.ts LN:79
 */
state.source.params = { ...state.source.params, ...source.data[link].query };
/**
 * @frontity/wp-source/src/libraries/handlers/postType.ts LN:52
 */
if (!state.source.get(route).id || force) {
  // ...
}

I would love to get advice from the community on a more effective / efficient way to set the state within actions. I could not figure out how to directly add to state.source.params via packages/mars-theme/src/components/post.js. This was the best I could figure out.

I’m also going to file an issue so that the force parameter passes through and actually fetches new data when forced. Hope this helps someone get password protected pages / posts working on their sites.

For those interested, the force update is being tracked here: https://github.com/frontity/frontity/issues/577