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

Hi, Iā€™m curious to how you were able to modify wp-source? Because Iā€™m trying to access password protected posts in frontity, but am not sure how. But this seems to look like exactly what Iā€™m trying to do that you were able to accomplish. It would be great if you can share to how you did it. Thanks in advance.

I know this is an old post but hope you see this :slight_smile:

I modified the mars-theme with some edits to the packages/mars-theme/src/post.js file:

First I created a place for users to input a password in render:

      { 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 then added a handler to submit the password:

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

Youā€™ll need to import useRef from React to access the input values. Hope this helps,

Thanky for the reply :slight_smile:
I decided to try it out with the Mars theme, but it doesnā€™t seem to work, it just displays an empty content below. Is there maybe something Iā€™m missing, that also needs to be added?

I added it like this:
<Html2React html=

      { 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 then on line 39 I added the following:

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 });

}

And then added ā€œimport { useEffect, useRef } from ā€œreactā€;ā€

Hi again,
I was actually able to get it to work, but nothing happens when pressing the Submit button next to the password field. Do you have an idea what Iā€™m missing to make it work?

Did you add something to functions in wordpress to get this to work?

Itā€™s been a long time so I donā€™t know off the top of my head. But, looking at my git history I think I only changed the post.js file I shared above.

Ok no problem. Sorry for all the questions just found this post and it was exactly what I was looking for :slightly_smiling_face: