The Role Paywall module allows site administrators to hide premium content from users that don't have access to it. This is performed on a per-field basis so the user will still see other parts of the page.

Access to content is determined based on access rules which are evaluated when the entity is viewed in the full view mode. This module comes with a User Role and Permission access rule and provides plugin support for additional rules to be contributed.

Project link

https://www.drupal.org/project/role_paywall

Git instructions

git clone --branch '2.1.x' https://git.drupalcode.org/project/role_paywall.git

Comments

akoepke created an issue. See original summary.

simonbaese’s picture

Status: Needs review » Needs work

I recommend running some static analysis with PHPStan (see PHPStan for Drupal).

You can get it with composer via:

composer require --dev phpstan/phpstan \                     
  phpstan/extension-installer \
  mglaman/phpstan-drupal \
  phpstan/phpstan-deprecation-rules

Then run:

vendor/bin/phpstan analyze PATH_TO_MODULE --memory-limit 256M --level 0

Getting the level up to 5 is quiet healthy. With my configuration with PHP8.0 your module returns 8 errors on level 0 and 24 errors on level 5. I ignore some errors in a phpstan.neon file, which you can place in your root directory. It looks like this:

parameters:
  level: 5
  ignoreErrors:
    # new static() is a best practice in Drupal, so we cannot fix that.
    - "#^Unsafe usage of new static#"
avpaderno’s picture

Status: Needs work » Needs review

A review needs to report what needs to be changed, although the review isn't required to be complete, nor to point out every single line that needs to be changed, especially when the same "error" is repeated more than once.

avpaderno’s picture

Thank you for applying! Reviewers will review the project files, describing what needs to be changed.

Please read Review process for security advisory coverage: What to expect for more details and Security advisory coverage application checklist to understand what reviewers look for. Tips for ensuring a smooth review gives some hints for a smother review.

To reviewers: Please read How to review security advisory coverage applications, What to cover in an application review, and Drupal.org security advisory coverage application workflow.

avpaderno’s picture

Status: Needs review » Needs work
  • This is a quick review that doesn't mean to be complete
  • Each point describes what needs to be changed to have secure code that correctly uses the Drupal API and that follows the Drupal coding standards
  • The review points are listed in order of importance as much as possible: first the points about security issues, then the points about code that doesn't correctly use the Drupal API; the points about wrong branch names, branch names that should be avoided because they are too similar, or code that doesn't follow the Drupal coding standards are listed for last
  • What reported for each point could require changing more than a file, or more lines in the same file, not only the shown lines.
          return [
            'role_paywall' => [
              'title' => t('Role Paywall'),
              'description' => t('Configuration for the barrier block has changed. Please <a href="@edit_url">edit the block settings</a> and check the new <strong>Role Paywall</strong> visibity option.', $vars),
              'severity' => REQUIREMENT_ERROR,
            ],
          ];

The correct placeholders for URLs is :edit_url as described in FormattableMarkup::placeholderFormat().

  /**
   * The Role Paywall Access Rule plugin being configured.
   *
   * @var \Drupal\role_paywall\Plugin\RolePaywallAccessRulePluginInterface
   */
  private $plugin;

  /**
   * The Entity Type we are configuring this plugin for.
   *
   * @var string
   */
  private $entityType;

Private properties should be avoided in form classes, as those cause issues when the form instance is cached.

  public static function create(ContainerInterface $container) {
    $instance = parent::create($container);
    $instance->setPluginManager($container->get('plugin.manager.role_paywall_access_rule'));
    return $instance;
  }

The task of the create() method is passing the injected dependencies to the class constructor.

Test classes go all in the tests/src directory.

  /**
   * {@inheritdoc}
   */
  public function __construct(EntityTypeManagerInterface $entity_manager) {
    $this->entityTypeManager = $entity_manager;
  }

The documentation comment for the class constructor is the same documentation comment for a method that isn't inherited from the parent class or defined in a interface.

  /**
   * Get a list of all available content entities.
   */

The first line in the documentation comment should use a verb in the third person singular. The rest of the comment should document the parameters and the returned value.

akoepke’s picture

Status: Needs work » Needs review

Thank you @simonbaese and @apaderno for your feedback.

I have gone through the issues reported by PHPStan and have no errors at level 5 now.

phpstan analyze .\web\modules\contrib\role_paywall\ --memory-limit 256M --level 5
Note: Using configuration file phpstan.neon.
 19/19 [============================] 100%



 [OK] No errors

@apaderno, the issues that you reported have all been resolved.

Can you please review the changes on commit 2cc02a9c and let me know if there is anything further required.

avpaderno’s picture

Status: Needs review » Reviewed & tested by the community
  public static function create(ContainerInterface $container) {
    $instance = parent::create($container);
    $instance->pluginManager = $container->get('plugin.manager.role_paywall_access_rule');
    return $instance;
  }

The code is still not calling the class constructor. The code should be similar to the code used from RolePaywallPluginSettings::create().

  public static function create(ContainerInterface $container) {
    return new static($container->get('plugin.manager.role_paywall_access_rule'));
  }
avpaderno’s picture

Assigned: Unassigned » avpaderno
Status: Reviewed & tested by the community » Fixed

Thank you for your contribution! I am going to update your account.

These are some recommended readings to help with excellent maintainership:

You can find more contributors chatting on the Slack #contribute channel. So, come hang out and stay involved.
Thank you, also, for your patience with the review process.
Anyone is welcome to participate in the review process. Please consider reviewing other projects that are pending review. I encourage you to learn more about that process and join the group of reviewers.

I thank all the dedicated reviewers as well.

akoepke’s picture

Hi @apaderno, thanks for the feedback and for processing my application.

As for the code not calling the class constructor, this is handled in the parent::create call.

My code was based on this example: https://www.drupal.org/docs/drupal-apis/services-and-dependency-injectio...

/**
 * {@inheritdoc}
 */
public static function create(ContainerInterface $container) {
  // Instantiates this form class.
  $instance = parent::create($container);
  $instance->currentUser = $container->get('current_user');
  return $instance;
}

Does this documentation need to be updated?

avpaderno’s picture

That example is wrong.
Any class that implements ContainerInjectionInterface::create() calls the class constructor. The documentation itself says:

The factory should pass any needed dependencies into the constructor of this class, but not the container itself.

That's what Drupal core does, for example in CronForm::create() or RegionalForm::create().

public static function create(ContainerInterface $container) {
  return new static(
    $container->get('config.factory'), $container->get('country_manager')
  );
}
akoepke’s picture

Hi @apaderno,

I had a look through the code for many of the forms in core and see that you are correct. I could only find one single example where the same method was used.

https://api.drupal.org/api/drupal/core%21modules%21update%21src%21Update...

I have updated the code for my forms to use the dependency injection correctly.

I have submitted an issue along with a merge request to fix the UpdateSettingsForm class - #3275216: Fix UpdateSettingsForm dependency injection.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.