Search code examples
apostrophe-cms

Public User Function to change Values


I would like to add a button to allow the public user to save simple true/false boolean values in their session and change things in the template according to those decisions. I think there are quite a few scenarios where something like this applies, like light/dark theme choice, "are you over 18" age queries or GDPR/Privacy stuff, where google analytics scripts need to be loaded only after the users decision.

For example I when I want to add a theme choice for public users.

This are my fields in my apostrophe-palette-global:

// lib/modules/apostrophe-palette-global
module.exports = {
  paletteFields: [

    {
      name: 'backgroundColorLight',
      label: 'Light Background',
      type: 'color',
      selector: [
        '.sections-content.light'
      ],
      property: 'background-color',
    },
    {
      name: 'backgroundColorDark',
      label: 'Dark Background',
      type: 'color',
      selector: [
        '.sections-content.dark'
      ],
      property: 'background-color',
    }
  ]
};

Create the template is pretty straightforward...

// lib/modules/apostrophe-pages/views/pages/home.html

{% extends "layout.html" %}

{% block main %}
  
  <div class="sections-content
// Define data scene only for user?
    {% if data.user.lightTheme %} 
      dark
    {% else %}
      light
    {% endif %}
  ">
    {{ apos.area(data.page, 'sectionArea', {
      limit: 1,
      widgets: {
        'sections': {
          pageShadow: data.page.shadow,
          addLabel: 'Add Sections',
          editLabel: 'Change Sections',
          controls: {
            movable: false,
            removable: true,
            position: 'top-left'
          }
        }
      }
    }) }}
  </div>

{% endblock %}

But I wasn't able until now to add a button which is accessible for everyone ( anon, user, admin ) and in this case would change the value of data.user.lightTheme where this value is only an example because I don't know how to store values in the public session, it won't be that easy for shure because I have to store the values via cookie or window.localStorage.

But is there an apostrophe way to do that and if it is, do you have some examples for that? That would be really helpful...


Solution

  • Apostrophe doesn't have a preference for reading/writing a browser storage endpoint, you'll have to roll your own with custom front end scripts that read/write and maintain your state.

    Below are some more detailed notes based on your code samples, you might not need this if you have a pretty good grasp on what to do when the answer to your original question is "no" :)

    1. The way you're handling theme styles via palette seems good, although I would alter your selectors to follow something higher in the DOM, like
    paletteFields: [
      {
        name: 'backgroundColorLight',
        label: 'Light Background',
        type: 'color',
        selector: [
          '.light .sections-content'
        ],
        property: 'background-color',
      }
    ]
    

    This way you only have to attach your theme class to some upper wrapper, possibly body

    1. If you're relying on the user's localStorage the server isn't going to know about it, so it won't be part of anything passed into your nunjucks templates. You'll need to handle this from a normal front end script. If you haven't pushed a custom script the quick how-to is here. Once you get your custom script pushed you'll want to read and write from your storage of choice (cookie, localStorage, sessionStorage). The basic flow might look something like:
    • Read the storage and look for the user's theme preference. If nothing is found, set or assume a default.
    • From your script, set the theme as a class on some high up wrapper in the DOM that all your style-able components will inherit from.
    • If no preference was found initially, present the user with some custom UI that lets them make a choice. (this part could be infinitely specific)
    • Write the user's choice to back to storage for the next page load.