Giter Club home page Giter Club logo

users's Introduction

Bolt Users Extension

The Bolt users extension allows you to add front-end users to your website.

Here is a list of things that the extension allows:

  • Define groups of users, allow them to register and login
  • Limit ContentTypes (pages) to be visible only by users belonging to a certain group
  • Define user fields and allow them to edit their own profile

Installation

To install this extension, simply run the following terminal command from your root folder:

composer require bolt/users

Basic usage

To limit a ContentType to a specific group of users, say ROLE_MEMBER, do the following:

  1. Define your user group in config/extensions/bolt-usersextension.yaml:
groups:
  ROLE_MEMBER:
    redirect_on_register: homepage # Provide either a route name, or a URL
    redirect_on_login: / # Provide either a route name, or a URL
    initial_status: enabled # Once a user registers, he/she is automatically allowed to login
  1. Limit the access to a certain ContentType, e.g. entries to that user group in config/contenttypes.yaml:
entries:
    name: Entries
    singular_name: Entry
    fields:
        # ... normal ContentType definition
    allow_for_groups: [ 'ROLE_MEMBER', 'ROLE_ADMIN' ]

Note: The allow_for_groups option is used to limit access to the ContentType (listing as well as record pages). It will only allow users who are logged in and have the correct permission to access those pages. Not even admins will be allowed to view those pages, hence why we add the ROLE_ADMIN group to ensure admins have view rights too.

  1. Allow users to register and to login

The extension allows you to include a registration form on any twig template. To add a registration form, just add the following to your twig file:

    {{ registration_form(group='ROLE_MEMBER') }}

This line below will render a registration form with username, password and email fields for the user to fill in. You must always specify the user group to which this form applies (in this case, ROLE_MEMBER). Users who register with that group will automatically receive access rights to ContentTypes limited to that group.

Currently, the registration_form function accepts the following options:

Option name Description Required / optional
group The group for the registering user. Must match a group defined in the extension config. required
withlabels If true, the label fields for each input will be included. Default is true. optional
labels An array used to override default labels. The key is the field name, e.g. username and the value is the label to be used. optional

To render the login form, use the following:

    {{ login_form() }}

The login function does not specify the group. The extension will try to authenticate the user with his/her credentials, and assign the correct group to that user. The login_form function accepts two optional arguments, withlabels and labels which work the same way as they do for the registration_form function.

User profiles

Sometimes, you want to do more with users than simply restrict access to certain pages. The extension allows you to define custom user fields by linking a ContentType to a user group.

For example, to define a date of birth to our 'ROLE_MEMBER' group, we would do the following:

  1. Define a members ContentType in config/contenttypes.ymal that will be used to store information about users.
members:
    name: Members
    singular_name: Member
    title_format: "{author.username}"
    fields:
      dob:
        type: date
    viewless: true

Then, edit the extension config in config/bolt-usersextension.yaml:

groups:
  ROLE_MEMBER:
    redirect_on_register: homepage
    redirect_on_login: /
    initial_status: enabled
    contenttype: members # Link the 'members' ContentType to the 'ROLE_MEMBER' group.

Now, users belonging to the ROLE_MEMBER group will be able to access their profile at /profile. You can customize the appearance of this page by customizing the record template for the members ContentType.

  1. Optionally, you may wish to allow members to edit their profiles. To do this, add the following to the config:
groups:
  ROLE_MEMBER:
    redirect_on_register: homepage
    redirect_on_login: /
    initial_status: enabled
    contenttype: members
    allow_profile_edit: true # If true, members will be able to edit their profiels on /profile/edit . You must specify the edit template below
    profile_edit_template: 'edit_profile.twig'

In this case, the edit_profile.twig file, located in the public/theme/your-theme/ directory, may contain any regular twig template. Here is a basic example of the edit form that you can include:

<form method="post">
    {% for field in record.fields %}
        <label for="fields[{{ field.name }}]"></label>
        {% if field.type === 'text' %}
            <input type="text" name="fields[{{ field.name }}]" value="{{ field.parsedValue }}" />
        {% elseif field.type === 'textarea' %}
            <textarea name="fields[{{ field.name }}]">{{ field.parsedValue }}</textarea>
        {% elseif field.type === 'checkbox' %}
            <input type="checkbox" name="fields[{{ field.name}}]" value="{{ field.parsedValue }}" />
        {% elseif field.type === 'date' %}
            <input type="date" name="fields[{{ field.name }}]" value="{{ field.parsedValue }}" />
        {% endif %}
    {% endfor %}

    <!-- The input fields below are required for Bolt to process the form. Do not change them -->
    <input type="hidden" name="_csrf_token" value="{{ csrf_token('editrecord') }}">
    <input type="hidden" name="_edit_locale" value="{{ user.locale }}">
    <input type="hidden" name="status" value="published">
    <input type="submit" value="save">
</form>

Customizing the register and login form appearance

If the customization options available in the registration_form and login_form functions are not enough, you may wish to use the following functions:

For registration:

Function Description
registration_form_username Renders the username field
registration_form_password Renders the password field
registration_form_email Renders the email field
registration_form_group Renders a hidden field for the user's group.
registration_form_csrf Renders a hidden field that contains a CSRF token.
registration_form_submit Renders the submit button

For logging in:

Function Description
login_form_username Renders the username field
login_form_password Renders the password field
login_form_csrf Renders a hidden field that contains a CSRF token.
login_form_submit Renders the submit button
login_redirect_url redirect to "/" after submit

Each field function above takes an optional withlabel argument and the labels argument that is also used by registration_form.

users's People

Contributors

i-valchev avatar bobdenotter avatar crim3hound avatar fatguytyson avatar oportier avatar rvey avatar jurjenvn avatar

Stargazers

 avatar David Krause avatar  avatar  avatar  avatar  avatar Hicham Taoufikallah avatar Omar López avatar

Watchers

James Cloos avatar  avatar  avatar

users's Issues

Is this meant to replace standard roles in bolt4?

Is this extension meant to be used as a replacement of the standard roles or in addition to them?
Since I can only assign one role per user I'm not really sure how to deal with the roles ROLE_ADMIN, ROLE_EDITOR and ROLE_USER after setting up the extension.

Email confirmation

Hi!
In the config, it's possible to set the "initial_status" to "email_confirmation", but i can't see any code who effectively send mail and activate the user only after it's mail is confirm.

Do i miss something ?

How can i implements this ? i thought to register a doctrine event to listen when the user is added.
Is it the best way ?

Form Label customization errors

Hi,

If you want to customize register or login form labels, you can inject in twig function {{ login_form() }} an array to define Username or Password labels.

Here is a way to do it beacause of a bug in src/Twig/LoginFormExtension.php:

{% set formParams = { 0: 'username', 1: 'password', 'username': 'Identifiant', 'password': 'Mot de passe' } %}
{{ login_form(true, formParams) }}

Why this ugly definition in formParms array with keys 0 and 1 set with respective values username and password ?

In src/Twig/LoginFormExtension.php and pricisely getUsernameField() and getPasswordField(), in_array() is not the correct way to check if username or password are set.
https://github.com/bolt/users/blob/master/src/Twig/LoginFormExtension.php#L67

The right way should be this one:

$text = array_key_exists('username', $labels) ? $labels['username'] : 'Username';

As Bob suggested, having the login and register forms build with Symfony Form would be easier and more simple to customize. For instance, having a id in the

tag or having a defined value in the username would be cool add-ons.

Thanks for your job and this nice extension !

Records for storing additional user data never get created

This is related to Issue #7 and PR #8.

I've taken some time to fiddle with the code in FrontendUsersProfileController.php and found that a bug seems to be in the record creation bit. I have rewritten my getUserRecord() function as follows:

private function getUserRecord(ContentType $contentType): Content
    {
        /** @var User $user */
        $user = $this->getUser();
        
        // access record if available
        $contentTypeSlug = $this->getExtension()->getExtConfig('contenttype', $user->getRoles()[0]);
        $contentType = $this->getBoltConfig()->getContentType($contentTypeSlug);

        $content = $this->contentRepository->findBy([
            'author' => $user,
            'contentType' => $contentType->getSlug(),
        ]);
        
        // bug in record creation bit
        if (empty($content)) {
            $content = new Content($contentType);
            $content->setAuthor($user);
            $content->setPublishedAt(new \DateTime());
            $content->setStatus(Statuses::PUBLISHED);
            $contentTypeName = strtolower($contentType->get('name', $contentType->get('slug')));
            $content->setContentType($contentTypeName);
            $this->contentFillListener->fillContent($content);

        } elseif (is_iterable($content)) {
            $content = end($content);
        }

        return $content;
    }

I have found that when I manually create a Member record, rewriting the $content definition as follows fetches content as expected, thereby rendering a profile page.

$content = $this->contentRepository->findBy([
        'contentType' => $contentType->getSlug(),
]);

Adding back the author filter throws an error because $content is empty, indicating that the block of code is working as expected. However, if $content is empty, we are supposed to add the respective user record, but that doesn't happen. I believe the issue is in this bit of the code:

 if (empty($content)) {
            $content = new Content($contentType);
            $content->setAuthor($user);
            $content->setPublishedAt(new \DateTime());
            $content->setStatus(Statuses::PUBLISHED);
            $contentTypeName = strtolower($contentType->get('name', $contentType->get('slug')));
            $content->setContentType($contentTypeName);
            $this->contentFillListener->fillContent($content);

 } elseif (is_iterable($content)) {
            $content = end($content);
 }

        return $content;

I have tried working on a function to generate the record during user registration, but I've been swimming in exceptions. I have also tried using a standalone record creation function in FrontendUsersProfileController.php as follows...

    private function new(ContentType $contentType): Content
    {
        /** @var User $user */
        $user = $this->getUser();

        $contentTypeSlug = $this->getExtension()->getExtConfig('contenttype', $user->getRoles()[0]);
        $contentType = $this->getBoltConfig()->getContentType($contentTypeSlug);
        
        $content = new Content($contentType);
        $content->setAuthor($user);
        $content->setCreatedAt(new \DateTime());
        $content->setPublishedAt(new \DateTime());
        $content->setStatus(Statuses::PUBLISHED);
        $contentTypeName = strtolower($contentType->get('name', $contentType->get('slug')));
        $content->setContentType($contentTypeName);
        $this->contentFillListener->fillContent($content);
        
        dump($content);
        return $content;
    }

...called from of the getUserRecord() function as follows:

if (empty($content)) {
       return $this->new($contentType);
            
} elseif (is_iterable($content)) {
       $content = end($content);
}

Dumping $content outputs the following, but the record is never created, even though it seems as though the new Content() function is working.

_dump

The implication is that the /profile route throws an error that the content requested cannot be found (see screenshot 1 below), and the edit fields in /profile/edit are not generated because there is no record to edit (see screenshot 2 below).

Screenshot 2020-10-20 073236

Screenshot 2020-10-20 073406

Any help will be greatly appreciated. Thanks!

Appearance customization

Hello!
I'm trying to customize HTML returned by login_form() function. This needs some attributes inside tags, like class, id etc.
But there are only label parameters available, as long as for login_form_username, login_form_password functions made for customization.
Of course, I can write my own html/twig as I want, but how do I retrieve postUrl and redirectField that are used in src/Twig/LoginFormExtension.php ?
Sorry if this is a dumb question, I'm new to BoltCMS. Should I rewrite named class or there is more elegant way?

Fields in content type don't appears in /profile/edit

Hello,

I want to set up profile editing from the frontend.
As written in the doc, I created a contentType

members:
    slug: members
    name: Members
    singular_name: Member
    title_format: "{author.username}"
    fields:
        dob:
            type: date
        adresse:
            type: text
        ville:
            type: text
     viewless: true

Then I inserted the following lines into bolt-userextension.yaml:

groups:
    ROLE_MEMBER:
        redirect_on_register: homepage
        redirect_on_login: /
        initial_status: enabled
        contenttype: members
        allow_profile_edit: true 
        profile_edit_template: 'edit_profile.twig'

And finally my twig file edit_profile.twig contains the following lines:

    <form method="post">
        {% for field in record.fields %}

            {{ dump(field) }}

            <label for="fields[{{ field.name }}]"></label>
            {% if field.type === 'text' %}
                <input type="text" name="fields[{{ field.name }}]" value="{{ field.parsedValue }}" />
            {% elseif field.type === 'textarea' %}
                <textarea name="fields[{{ field.name }}]">{{ field.parsedValue }}</textarea>
            {% elseif field.type === 'checkbox' %}
                <input type="checkbox" name="fields[{{ field.name}}]" value="{{ field.parsedValue }}" />
            {% elseif field.type === 'date' %}
                <input type="date" name="fields[{{ field.name }}]" value="{{ field.parsedValue }}" />
            {% endif %}
        {% endfor %}

        <!-- The input fields below are required for Bolt to process the form. Do not change them -->
        <input type="hidden" name="_csrf_token" value="{{ csrf_token('editrecord') }}">
        <input type="hidden" name="_edit_locale" value="{{ user.locale }}">
        <input type="hidden" name="status" value="published">
        <input type="submit" value="save">
    </form>

Nothing is displayed, according to the DUMP, record.fields only contains the slug :

[Bolt\Entity\Field\SlugField]{
  -id: 29
  +name: "slug"
  -sortorder: 0
  -version: null
  -content: [Bolt\Entity\Content]...
  -parent: null
  -fieldTypeDefinition: [Bolt\Configuration\Content\FieldType]
  -useDefaultLocale: true
  #translations: [Doctrine\ORM\PersistentCollection]
  #newTranslations: null
  #currentLocale: "fr"
  #defaultLocale: "fr"
}

Any ideals ?

Bolt\Entity\Content::setStatus() error when trying to access '/profile' and '/profile/edit' routes

I have installed the Bolt Users extension and would like to save additional user information in a contenttype as described in the documentation. I have implemented the appropriate contenttype, and the authentication flow works. However, when I try to access the profile view and profile edit pages, i.e. /profile and /profile/edit, the error below is thrown:

Argument 1 passed to Bolt\Entity\Content::setStatus() must be of the type string, null given, called in C:\laragon\www\xxxxx\vendor\bolt\core\src\Entity\Content.php on line 189 (see screenshot)

I'm not sure what the issue could be. Any assistance would be greatly appreciated.

Screenshot 2020-10-12 165917

No exception handling for routes when sessions are nullified or inactive

Trying to retrieve the ContentType slug using $contentTypeSlug = $this->getExtension()->getExtConfig('contenttype', $user->getRoles()[0]); in FrontendUsersProfileController.php throws the exception Call to a member function getRoles() on null.

This happens when a user tries to access profile routes without an active session, either because it expired or was invalidated.

session_issue

I have found a solution for this issue and will share for review.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.