foldershare-8.x-1.2/src/Plugin/FolderShareCommand/Share.php

src/Plugin/FolderShareCommand/Share.php
<?php
 
namespace Drupal\foldershare\Plugin\FolderShareCommand;
 
use Drupal\Core\Cache\Cache;
use Drupal\Core\Url;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\Entity\User;
 
use Drupal\foldershare\Constants;
use Drupal\foldershare\Settings;
use Drupal\foldershare\Utilities\UserUtilities;
use Drupal\foldershare\Entity\FolderShare;
use Drupal\foldershare\Entity\FolderShareAccessControlHandler;
 
/**
 * Defines a command plugin to change share grants on a root item.
 *
 * The command sets the access grants for the root item of the selected
 * entity. Access grants enable/disable view and author access for
 * individual users.
 *
 * Configuration parameters:
 * - 'parentId': the parent folder, if any.
 * - 'selectionIds': selected entity who's root item is shared.
 * - 'grants': the new access grants.
 *
 * @ingroup foldershare
 *
 * @FolderShareCommand(
 *  id              = "foldersharecommand_share",
 *  label           = @Translation("Share"),
 *  menuNameDefault = @Translation("Share..."),
 *  menuName        = @Translation("Share..."),
 *  description     = @Translation("Share selected top-level files and folders, and their contents."),
 *  category        = "settings",
 *  weight          = 10,
 *  userConstraints = {
 *    "authenticated",
 *  },
 *  parentConstraints = {
 *    "kinds"   = {
 *      "rootlist",
 *    },
 *    "access"  = "view",
 *  },
 *  selectionConstraints = {
 *    "types"   = {
 *      "one",
 *    },
 *    "kinds"   = {
 *      "any",
 *    },
 *    "access"  = "share",
 *  },
 * )
 */
class Share extends FolderShareCommandBase {
 
  /*--------------------------------------------------------------------
   *
   * Configuration.
   *
   *--------------------------------------------------------------------*/
 
  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    // Include room for the new grants in the configuration.
    $config = parent::defaultConfiguration();
    $config['grants'] = [];
    return $config;
  }
 
  /*--------------------------------------------------------------------
   *
   * Configuration form setup.
   *
   *--------------------------------------------------------------------*/
 
  /**
   * {@inheritdoc}
   */
  public function hasConfigurationForm() {
    return TRUE;
  }
 
  /**
   * {@inheritdoc}
   */
  public function getDescription(bool $forPage) {
    // The description varies for page vs. dialog:
    //
    // - Dialog: The description is longer and has the form "Grant shared
    //   access to NAME and its contents."
    //
    // - Page: The description is as for a dialog, except the name is not
    //   included because it is already in the title.
    $selectionIds = $this->getSelectionIds();
    $item = FolderShare::load(reset($selectionIds));
 
    $description = [];
 
    if ($forPage === TRUE) {
      // Page description. The page title already gives the name of the
      // item. Don't include the item's name again here.
      if ($item->isFolder() === TRUE) {
        $description[] = t(
          'Grant shared access to this folder and its contents.',
          [
            '@name' => $item->getName(),
          ]);
      }
      else {
        $description[] = t(
          'Grant shared access to @operand.',
          [
            '@operand' => FolderShare::translateKind($item->getKind()),
          ]);
      }
    }
    else {
      // Dialog description. Include the name of the item to be changed.
      if ($item->isFolder() === TRUE) {
        $description[] = t(
          'Grant shared access to "@name" and its contents.',
          [
            '@name' => $item->getName(),
          ]);
      }
      else {
        $description[] = t(
          'Grant shared access to "@name".',
          [
            '@name' => $item->getName(),
          ]);
      }
    }
 
    $description[] = t(
      '%view access allows users to view, copy, and download. %author access also allows users to edit, delete, move, and upload.',
      [
        '%view'   => t('View'),
        '%author' => t('Author'),
      ]);
 
    return $description;
  }
 
  /**
   * {@inheritdoc}
   */
  public function getTitle(bool $forPage) {
    // The title varies for page vs. dialog:
    //
    // - Dialog: The title is short and has the form "Share OPERAND",
    //   where OPERAND is the kind of item (e.g. "file"). By not putting
    //   the item's name in the title, we keep the dialog title short and
    //   avoid cropping or wrapping.
    //
    // - Page: The title is longer and has the form "Share "NAME"?"
    //   This follows Drupal convention.
    $selectionIds = $this->getSelectionIds();
    $item = FolderShare::load(reset($selectionIds));
 
    if ($forPage === TRUE) {
      // Page title. Include the name of the item.
      return t(
        'Share "@name"',
        [
          '@name' => $item->getName(),
        ]);
    }
 
    // Dialog title. Include the operand kind.
    return t(
      'Share @operand',
      [
        '@operand' => FolderShare::translateKind($item->getKind()),
      ]);
  }
 
  /**
   * {@inheritdoc}
   */
  public function getSubmitButtonName() {
    return t('Save');
  }
 
  /*--------------------------------------------------------------------
   *
   * Configuration form.
   *
   *--------------------------------------------------------------------*/
 
  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(
    array $form,
    FormStateInterface $formState) {
 
    //
    // Define form element names/classes.
    // ----------------------------------
    // Most form elements have specific names/classes. Names enable values
    // to be retreived easily. Classes enable custom styling.
    $shareForm             = Constants::MODULE . '_share_form';
    $shareFormWrapper      = Constants::MODULE . '_share_form_wrapper';
    $shareTable            = Constants::MODULE . '_share_table';
    $addUserForm           = Constants::MODULE . '_share_add_user';
    $addUserField          = Constants::MODULE . '_share_add_user_name';
    $addUserFieldAndButton = Constants::MODULE . '_share_add_user_name_and_button';
    $addUserButton         = Constants::MODULE . '_share_add_user_button';
    $addUserDescription    = Constants::MODULE . '_share_add_user_description';
    $addUserMessages       = Constants::MODULE . '_share_add_user_messages';
 
    $formInput = $formState->getUserInput();
 
    //
    // Get current user permissions.
    // -----------------------------
    // Does the current user have permission to share with other users
    // and/or share with the public?
    $currentUser = \Drupal::currentUser();
 
    $hasShareWithUsersPermission = AccessResult::allowedIfHasPermission(
      $currentUser,
      Constants::SHARE_PERMISSION)->isAllowed();
 
    $hasShareWithPublicPermission = AccessResult::allowedIfHasPermission(
      $currentUser,
      Constants::SHARE_PUBLIC_PERMISSION)->isAllowed();
 
    // If the current user doesn't have permission to share content with
    // other users or with the public, then the share form should not have
    // been presented. Since it has been, show an error message.
    if ($hasShareWithUsersPermission === FALSE &&
        $hasShareWithPublicPermission === FALSE) {
      $form['nopermission'] = [
        '#type'  => 'html_tag',
        '#tag'   => 'p',
        '#value' => t('You do not have permission to share files and folders with other users or the public.'),
      ];
 
      unset($form['actions']['submit']);
 
      return $form;
    }
 
    //
    // Get user info.
    // --------------
    // On the first form build, get the list of grants from the selected
    // item. On subsequent calls, use a saved list that's been updated with
    // added and removed users.
    //
    // The list has UID keys and values that are arrays with one
    // or more of:
    //
    // '' = no grants.
    // 'view' = granted view access.
    // 'author' = granted author access.
    //
    // The returned array cannot be empty. It always contains at least
    // the owner of the folder, who always has 'view' and 'author' access.
    $selectionIds = $this->getSelectionIds();
    $item = FolderShare::load(reset($selectionIds));
 
    $grants = $formState->get('grants');
    if (empty($grants) === TRUE) {
      $grants = $item->getAccessGrants();
      $formState->set('grants', $grants);
    }
 
    // Load all referenced users. We need their display names for the
    // form's list of users. Add the users into an array keyed by
    // the display name, then sort on those keys so that we can create
    // a sorted list in the form.
    $loadedUsers = User::loadMultiple(array_keys($grants));
    $users = [];
    foreach ($loadedUsers as $user) {
      if ($user === NULL) {
        // User cannot be loaded. The item's grants list probably references
        // a user ID for an account that has been deleted. Silently ignore
        // the deleted user.
        continue;
      }
      $users[$user->getDisplayName()] = $user;
    }
 
    ksort($users, SORT_NATURAL);
 
    // Get the anonymous user.
    $anonymousUser = User::getAnonymousUser();
 
    // Get the user ID of the special primary site admin. There can be
    // additional site admins, and it is even possible to delete the primary
    // site admin (though that is not advised).
    $siteAdminId = 1;
 
    // Save the root folder for use when the form is submitted later.
    $this->entity = $item;
 
    // Get the item's owner.
    $ownerId = $item->getOwnerId();
    $owner   = User::load($ownerId);
 
    //
    // Table of users and grants.
    // --------------------------
    // Create a table that has a first column showing user names, and a
    // second column showing access grants.
    $form[$shareForm] = [
      '#type'         => 'container',
      '#name'         => $shareForm,
      '#prefix'       => '<div id="' . $shareFormWrapper . '">',
      '#suffix'       => '</div>',
      '#attributes'      => [
        'class'          => [$shareForm],
      ],
 
      $shareTable     => [
        '#type'       => 'table',
        '#name'       => $shareTable,
        '#attributes' => [
          'class'     => [$shareTable],
        ],
        '#responsive' => FALSE,
        '#sticky'     => TRUE,
        '#header'     => [
          t('User'),
          t(
            '<span>@none</span><span>@view</span><span>@author</span>',
            [
              '@none'   => t('None'),
              '@view'   => t('View'),
              '@author' => t('Author'),
            ]),
        ],
      ],
    ];
 
    $rows = [];
    $uids = [];
 
    //
    // Add row for anonymous.
    // ----------------------
    // If Sharing with anonymous is allowed for this user, include a row
    // to do so at the top of the table.
    if ($hasShareWithPublicPermission === TRUE) {
      if (isset($grants[(int) $anonymousUser->id()]) === FALSE) {
        // There is no entry yet for anonymous. Create one, but with neither
        // view or author access.
        $grants[0] = [];
      }
 
      $r = $this->buildRow(
        $anonymousUser,
        $owner,
        $grants[(int) $anonymousUser->id()]);
 
      // buildRow() can return an empty array if the anonymous user has not
      // been given view permission for files and folders.
      if (empty($r) === FALSE) {
        $rows[] = $r;
      }
    }
 
    //
    // Add rows for users granted access.
    // ----------------------------------
    // If sharing with other users is allowed for this user, include a list
    // of users currently granted access.
    if ($hasShareWithUsersPermission === TRUE) {
      foreach ($users as $user) {
        $uid = (int) $user->id();
 
        // Do not add table rows for:
        // - Anonymous.
        // - Blocked accounts.
        // - The primary site admin.
        // - The file/folder owner.
        //
        // Anonymous access is already handled above and requires a separate
        // permission.
        //
        // Blocked accounts can be on a grants list if the account was blocked
        // recently. But once blocked, this form no longer shows the account
        // and the account is silently removed from the grants list when the
        // form is saved.
        //
        // The primary site admin always had access, so they don't have to be
        // on a share list. Adding them looks odd, so they are silently skipped.
        //
        // The owner always has access to their content, so they don't need
        // to be listed here.
        if ($user->isAnonymous() === TRUE ||
            $user->isBlocked() === TRUE ||
            $uid === $siteAdminId ||
            $uid === $ownerId) {
          continue;
        }
 
        // Add the row.
        $r = $this->buildRow(
          $user,
          $owner,
          $grants[$uid]);
 
        if (empty($r) === FALSE) {
          $rows[] = $r;
          $uids[] = $uid;
        }
      }
    }
 
    // If the above code did not add any rows, then there is currently no one
    // granted shared access. Say so.
    //
    // If the above code added rows, excluding anonymous, then add a table
    // description.
    if (count($rows) <= 0) {
      $rows[] = [
        'User'                  => [
          '#markup'             => t('No users have shared access.'),
          '#wrapper_attributes' => [
            'colspan'           => 2,
          ],
        ],
      ];
    }
    elseif ($hasShareWithUsersPermission === TRUE) {
      $form[$shareForm]['description'] = [
        '#type'         => 'html_tag',
        '#tag'          => 'p',
        '#value'        => t(
          'Users with "%none" selected are automatically removed from this list when the form is saved.',
          [
            '%none' => t('None'),
          ]),
        '#attributes'   => [
          'class'       => ['description'],
        ],
      ];
    }
 
    if (count($rows) != 0) {
      $form[$shareForm][$shareTable] = array_merge(
        $form[$shareForm][$shareTable],
        $rows);
    }
 
    //
    // Add user autocomplete field and button.
    // ---------------------------------------
    // If sharing with other users is allowed for this user, include an
    // 'Add a user' autocomplete field used to add users to the share list.
    if ($hasShareWithUsersPermission === TRUE) {
      // If the text field is empty, the 'Add' user button is disabled by
      // Javascript attached to the form.
      $routeParameters =
        $form['actions']['submit']['#ajax']['url']->getRouteParameters()['encoded'];
 
      $description = '';
      switch (Settings::getUserAutocompleteStyle()) {
        default:
        case 'none':
        case 'name-only':
          $description = t('Enter the account name of a user.');
          break;
 
        case 'name-email':
        case 'name-masked-email':
          $description = t('Enter the account name or email address of a user.');
          break;
      }
 
      $defaultUser = '';
      if (isset($formInput[$addUserField]) === TRUE) {
        $defaultUser = $formInput[$addUserField];
      }
 
      $form[$shareForm][$addUserForm] = [
        '#type'               => 'container',
        '#tree'               => TRUE,
        '#name'               => $addUserForm,
        '#attributes'         => [
          'class'             => [$addUserForm],
        ],
        'label'               => [
          '#type'             => 'label',
          '#title'            => t('Add a user'),
        ],
        $addUserFieldAndButton => [
          '#type'             => 'container',
          '#attributes'       => [
            'class'           => [$addUserFieldAndButton],
          ],
          $addUserField       => [
            '#type'           => 'textfield',
            '#maxlength'      => 256,
            '#default_value'  => $defaultUser,
            '#required'       => FALSE,
            '#name'           => $addUserField,
            '#attributes'     => [
              'autofocus'     => 'autofocus',
              'spellcheck'    => 'false',
              'class'         => [$addUserField],
            ],
          ],
          $addUserButton => [
            '#type'           => 'button',
            '#value'          => t('Add'),
            '#name'           => $addUserButton,
            '#attributes'     => [
              'class'         => [$addUserButton],
            ],
            '#ajax'           => [
              'callback'      => [$this, 'refreshConfigurationForm'],
              'event'         => 'click',
              'wrapper'       => $shareFormWrapper,
              'url'           => Url::fromRoute(
                'entity.foldersharecommand.plugin',
                [
                  'encoded'   => $routeParameters,
                ]),
              'options'       => [
                'query'       => [
                  'ajax_form' => 1,
                ],
              ],
            ],
          ],
        ],
        $addUserDescription   => [
          '#type'             => 'html_tag',
          '#tag'              => 'p',
          '#value'            => $description,
          '#attributes'       => [
            'class'           => [
              'description',
              $addUserDescription,
            ],
          ],
        ],
        $addUserMessages      => [
          '#type'             => 'status_messages',
        ],
      ];
 
      // If the site allows user autocomplete, set up the text field. Include
      // a list of users culled to skip:
      // - All users already with shared access.
      // - The current user, who always has access.
      // - Anonymous, who has a special row above.
      // - The primary site admin, who always has access.
      $autocompleteStyle = Settings::getUserAutocompleteStyle();
 
      if ($autocompleteStyle !== 'none') {
        $excludeUids = $uids;
        $excludeUids[] = (int) $currentUser->id();
        $excludeUids[] = (int) $anonymousUser->id();
        $excludeUids[] = $siteAdminId;
 
        $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#autocomplete_route_name'] =
          'entity.foldershare.userautocomplete';
 
        $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#autocomplete_route_parameters'] = [
          'excludeUids'    => $excludeUids,
          'excludeBlocked' => 1,
        ];
      }
    }
 
    return $form;
  }
 
  /**
   * Builds and returns a form row for a user.
   *
   * The form has three columns:
   * - User name.
   * - Access choice.
   * - Actions.
   *
   * The user name is the account's display name.
   *
   * The access choice is a pair of radio buttons for "view" and "access".
   *
   * If the given user does not have appropriate module permissions, they
   * cannot have either "view" or "access" and an empty array is returned.
   *
   * @param \Drupal\user\Entity\User $user
   *   The user for whom to build the row.
   * @param \Drupal\user\Entity\User $owner
   *   The owner of the current root item.
   * @param array $grant
   *   The access grants for the user.
   *
   * @return array
   *   The form table row description for the user, or an empty row if the
   *   user cannot be granted access because their account is blocked, they
   *   don't have module permissions, or they are the item's owner.
   */
  private function buildRow(
    User $user,
    User $owner,
    array $grant) {
 
    //
    // Get account attributes.
    // -----------------------
    // Watch for special users.
    $rowIsForAnonymous   = $user->isAnonymous();
    $rowIsForBlockedUser = $user->isBlocked();
    $rowIsForRootOwner   = ((int) $user->id() === (int) $owner->id());
 
    if ($rowIsForBlockedUser === TRUE && $rowIsForAnonymous === FALSE) {
      // Blocked non-anonymous users cannot be granted shared access.
      // Do not list them.
      return [];
    }
 
    if ($rowIsForRootOwner === TRUE) {
      // The owner always has access. Do not list them.
      return [];
    }
 
    //
    // Check row user permissions.
    // ---------------------------
    // Get what the row user is permitted to do based on permissions alone.
    $permittedToView =
      FolderShareAccessControlHandler::mayAccess('view', $user);
    $permittedToAuthor =
      FolderShareAccessControlHandler::mayAccess('update', $user);
 
    if ($permittedToView === FALSE) {
      // The user has not been given view access for the module's content.
      // They cannot be granted shared access then.
      return [];
    }
 
    //
    // Get grants for this root.
    // -------------------------
    // Check the access grants on the root item and see what the row user
    // has been granted to do, if anything.
    $currentlyGrantedView = in_array('view', $grant);
    $currentlyGrantedAuthor = in_array('author', $grant);
 
    // If the user is explicitly granted author access, then automatically
    // include view access.
    if ($currentlyGrantedAuthor === TRUE) {
      $currentlyGrantedView = TRUE;
    }
 
    if ($permittedToAuthor === FALSE) {
      // The row user doesn't have author permission, so any author grant
      // they have on the row's root is irrelevant.
      $currentlyGrantedAuthor = FALSE;
    }
 
    //
    // Build row
    // ---------
    // Start by creating the radio button options array based upon the grants.
    $radios = ['none' => ''];
    $default = 'none';
 
    if ($permittedToView === TRUE) {
      // User has view permissions, so allow a 'view' choice.
      $radios['view'] = '';
    }
 
    if ($permittedToAuthor === TRUE) {
      // User has author permissions, so allow a 'author' choice.
      $radios['author'] = '';
    }
 
    if ($currentlyGrantedView === TRUE) {
      // User has been granted view access.
      $default = 'view';
    }
 
    if ($currentlyGrantedAuthor === TRUE) {
      // User has been granted author access (which for our purposes
      // includes view access).
      $default = 'author';
    }
 
    // Create an annotated user name.
    $name = $user->getDisplayName();
    if ($rowIsForAnonymous === TRUE) {
      $nameMarkup = t('Everyone that can access this website');
    }
    else {
      $nameMarkup = t(
        '@name',
        [
          '@name' => $name,
        ]);
    }
 
    // Create the row. Provide the user's UID as the row value, which we'll
    // use later during validation and submit handling. Show a link to the
    // user's profile.
    $shareTableRow       = Constants::MODULE . '_share_table_row';
    $shareTableRowUser   = Constants::MODULE . '_share_table_row_user';
    $shareTableRowAccess = Constants::MODULE . '_share_table_row_access';
 
    return [
      'User' => [
        '#type'          => 'item',
        '#value'         => $user->id(),
        '#markup'        => $nameMarkup,
        '#attributes'    => [
          'class'        => [$shareTableRowUser],
        ],
      ],
 
      // Disable the row if the user has no permissions.
      'Access' => [
        '#type'          => 'radios',
        '#options'       => $radios,
        '#default_value' => $default,
        '#attributes'    => [
          'class'        => [$shareTableRowAccess],
        ],
      ],
 
      '#attributes'      => [
        'class'          => [$shareTableRow],
      ],
    ];
  }
 
  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(
    array &$form,
    FormStateInterface $formState) {
 
    //
    // Setup.
    // ------
    // Get user input.
    $trigger = $formState->getTriggeringElement();
    $formInput = $formState->getUserInput();
 
    $shareForm             = Constants::MODULE . '_share_form';
    $shareTable            = Constants::MODULE . '_share_table';
    $addUserForm           = Constants::MODULE . '_share_add_user';
    $addUserFieldAndButton = Constants::MODULE . '_share_add_user_name_and_button';
    $addUserField          = Constants::MODULE . '_share_add_user_name';
    $addUserButton         = Constants::MODULE . '_share_add_user_button';
 
    // Get the selection, which is always a single root list item.
    $selectionIds = $this->getSelectionIds();
    $item = FolderShare::load(reset($selectionIds));
    $ownerId = $item->getOwnerId();
 
    //
    // Determine reason validation was called.
    // ---------------------------------------
    // Validation is called on a 'Save' button click to submit the form,
    // or on an 'Add' button click to add a new user.
    $formIsForAdd = FALSE;
    if ($trigger['#name'] === $addUserButton) {
      $formIsForAdd = TRUE;
    }
 
    //
    // Validate current grants.
    // ------------------------
    // Whether or not the user has clicked 'Save' to submit changes to
    // the grant list, we still need to validate them and save them into
    // the form's configuration.
    //
    // Get the form's grants. These may differ from the item's original
    // grants in two ways:
    // - Added users will have an entry.
    // - Removed users will not have an entry.
    //
    // All entries that are not for added users will have the view/author
    // grants originally granted. Below we'll adjust those grants based upon
    // the radio buttons in the form.
    $grants = $formState->get('grants');
 
    // Get the original grants too.
    $originalGrants = $item->getAccessGrants($item);
 
    // Copy the owner's own original grants forward. The owner always has
    // access and should not have been listed in the form, but be safe.
    if (isset($originalGrants[$ownerId]) === TRUE) {
      $grants[$ownerId] = $originalGrants[$ownerId];
    }
 
    // Get the current list of users from the table.
    $entries = $formState->getValue($shareForm)[$shareTable];
 
    if (empty($entries) === FALSE) {
      if ($formIsForAdd === TRUE) {
        // The form has been submitted by clicking 'Add' to add a new user.
        // The form's table of users and grants is not final yet, so it is
        // premature to validate it.
        foreach ($entries as $entry) {
          $uid = $entry['User'];
          switch ($entry['Access']) {
            case 'view':
              $grants[$uid] = ['view'];
              break;
 
            case 'author':
              $grants[$uid] = [
                'view',
                'author',
              ];
              break;
 
            case 'none':
              $grants[$uid] = [];
              break;
 
            default:
              break;
          }
        }
      }
      else {
        // Validate that the new grants make sense.
        //
        // Loop through the form's table of users and access grants. For each
        // user, see if 'none', 'view', or 'author' radio buttons are set
        // and create the associated grant in a user-grant array.
        foreach ($entries as $entry) {
          $uid = $entry['User'];
 
          // Ignore any row for the root item owner. This shouldn't have been in
          // the table in the first place.
          if ($uid === $ownerId) {
            continue;
          }
 
          // Ignore blocked accounts that aren't anonymous. Blocked accounts
          // should not have been in the table in the first place.
          $user = User::load($uid);
          if ($user === NULL ||
              ($user->isBlocked() === TRUE && $user->isAnonymous() === FALSE)) {
            continue;
          }
 
          switch ($entry['Access']) {
            case 'view':
              // Make sure the user has view permission.
              if (FolderShareAccessControlHandler::mayAccess('view', $user) === TRUE) {
                $grants[$uid] = ['view'];
              }
              break;
 
            case 'author':
              // Make sure the user has author permission.
              if (FolderShareAccessControlHandler::mayAccess('update', $user) === TRUE) {
                // Author grants ALWAYS include view grants.
                $grants[$uid] = [
                  'view',
                  'author',
                ];
              }
              break;
 
            case 'none':
              // Remove any user that has not been granted any permission.
              unset($grants[$uid]);
              break;
 
            default:
              break;
          }
        }
      }
    }
 
    // Save the updated grants back into the form and into the command's
    // configuration.
    $formState->set('grants', $grants);
    $this->configuration['grants'] = $grants;
 
    //
    // Handle add user.
    // ----------------
    // When the trigger is the add user button, get the entered text for
    // a user to add. The text can be an account name or email address.
    // Add the user to the grants list, then rebuild the form.
    if ($formIsForAdd === TRUE) {
      // Get the entered text for the user to add.
      $name = $formInput[$addUserField];
 
      // Remove leading white space.
      $cleanedName = mb_ereg_replace('^\s+', '', $name);
      if ($cleanedName !== FALSE) {
        $name = $cleanedName;
      }
 
      // Remove trailing white space.
      $cleanedName = mb_ereg_replace('\s+$', '', $name);
      if ($cleanedName !== FALSE) {
        $name = $cleanedName;
      }
 
      if (empty($name) === TRUE) {
        // Empty user. Intentionally use an empty error message. This will
        // highlight the form field but not show a message. Such a message
        // would be redundant and just say "Enter an account name", which
        // is what the description under the field already says.
        $formState->setErrorByName(
          $shareForm,
          '');
 
        // Clear the name field.
        // Since a validation error does not rebuild the form, updating the
        // form's state does not work to insure the form name field is empty.
        $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#value'] = '';
        $formState->setRebuild();
        return;
      }
 
      //
      // Look up the user.
      // -----------------
      // Use the given name to find a user then add them to the grants list.
      $uid = UserUtilities::findUser($name);
      if ($uid === -1) {
        // Unknown user.
        $formState->setErrorByName(
          $shareForm,
          t(
            '"%name" is not a recognized account at this site.',
            [
              '%name' => $name,
            ]));
 
        // Insure the name field has the cleaned name.
        // Since a validation error does not rebuild the form, updating the
        // form's state does not work to insure the form is showing the cleaned
        // name. Set it into the form directly.
        $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#value'] = $name;
        $formState->setRebuild();
        return;
      }
 
      //
      // Validate user.
      // --------------
      // User's must be:
      // - Not the owner.
      // - Not the administrator.
      // - Not blocked.
      // - Have at least view permission on FolderShare content.
      // - Not already on the grants list.
      if ((int) $this->entity->getOwnerId() === $uid) {
        // Owner.
        if ((int) \Drupal::currentUser()->id() === $uid) {
          // The current user is the owner. Tell them they don't have to be
          // added to the list.
          $formState->setErrorByName(
            $shareForm,
            t('As the owner, you always have access and do not need to be added to the list.'));
        }
        else {
          // The current user is not the owner, but has permission to change
          // sharing on the item. They are probably an administrator. Tell
          // them they don't need to add the owner to the list.
          $formState->setErrorByName(
            $shareForm,
            t(
              '"%name" is the @kind owner, who always has access. They do not need to be added to the list.',
              [
                '%name' => $name,
                '@kind' => FolderShare::translateKind($this->entity->getKind()),
              ]));
        }
 
        // Clear the name field.
        // Since a validation error does not rebuild the form, updating the
        // form's state does not work to insure the form name field is empty.
        $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#value'] = '';
        $formState->setRebuild();
        return;
      }
 
      if ($uid === 1) {
        // Primary site admin.
        $formState->setErrorByName(
          $shareForm,
          t(
            '"%name" account is the site administrator, who always has access for administrative purposes. They cannot be added to the list.',
            [
              '%name' => $name,
            ]));
 
        // Clear the name field.
        // Since a validation error does not rebuild the form, updating the
        // form's state does not work to insure the form name field is empty.
        $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#value'] = '';
        $formState->setRebuild();
        return;
      }
 
      $user = User::load($uid);
      if ($user->isBlocked() === TRUE && $user->isAnonymous() === FALSE) {
        // Blocked user that is not anonymous.
        $formState->setErrorByName(
          $shareForm,
          t(
            'The "%name" account is disabled and cannot be added to the list.',
            [
              '%name' => $name,
            ]));
 
        // Clear the name field.
        // Since a validation error does not rebuild the form, updating the
        // form's state does not work to insure the form name field is empty.
        $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#value'] = '';
        $formState->setRebuild();
        return;
      }
 
      if (FolderShareAccessControlHandler::mayAccess('view', $user) === FALSE) {
        // User without view permission.
        if ($uid === 0) {
          // Anonymous.
          $formState->setErrorByName(
            $shareForm,
            t('Public visitors to the web site do not have permission to use files and folders. They cannot be added to the list.'));
        }
        else {
          $formState->setErrorByName(
            $shareForm,
            t(
              '"%name" does not have permission to use files and folders. They cannot be added to the list.',
              [
                '%name' => $name,
              ]));
        }
 
        // Clear the name field.
        // Since a validation error does not rebuild the form, updating the
        // form's state does not work to insure the form name field is empty.
        $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#value'] = '';
        $formState->setRebuild();
        return;
      }
 
      $grants = $formState->get('grants');
      if (isset($grants[$uid]) === TRUE) {
        // User already on list.
        $formState->setErrorByName(
          $shareForm,
          t(
            '"%name" is already on the list.',
            [
              '%name' => $name,
            ]));
 
        // Clear the name field.
        // Since a validation error does not rebuild the form, updating the
        // form's state does not work to insure the form name field is empty.
        $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#value'] = '';
        $formState->setRebuild();
        return;
      }
 
      //
      // Add user to pending grants.
      // ---------------------------
      // Add the user's ID to the list of pending grants. Default the user
      // to no granted access.
      $grants[$uid] = [];
      $formState->set('grants', $grants);
      $this->configuration['grants'] = $grants;
 
      // Clear the current form's saved radio button state because the number
      // of table rows in that state no longer matches the number or order of
      // grants. If we don't reset the form's input, the wrong radio button
      // values will be assigned to the newly ordered users and we get a mess.
      $formInput[$addUserField] = '';
      unset($formInput[$shareForm][$shareTable]);
      $formState->setUserInput($formInput);
 
      // Clear the name field.
      // Since a validation error does not rebuild the form, updating the
      // form's state does not work to insure the form name field is empty.
      $form[$shareForm][$addUserForm][$addUserFieldAndButton][$addUserField]['#value'] = '';
 
      $formState->setRebuild();
      return;
    }
 
    //
    // Finish.
    // -------
    // At this point, the grants have been updated to match the form. The
    // user has clicked 'Save'. Validate everything to be sure.
    try {
      $this->validateParameters();
    }
    catch (\Exception $e) {
      $formState->setErrorByName(
        $shareTable,
        $e->getMessage());
      return;
    }
  }
 
  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(
    array &$form,
    FormStateInterface $formState) {
 
    $this->configuration['grants'] = $formState->get('grants');
    $this->execute();
  }
 
  /**
   * Refreshes the configuration form after an AJAX request.
   *
   * @param array $form
   *   The configuration form.
   * @param \Drupal\Core\Form\FormStateInterface $formState
   *   The form state.
   *
   * @return array
   *   Returns the share form portion of the form.
   */
  public function refreshConfigurationForm(
    array &$form,
    FormStateInterface $formState) {
 
    $shareForm = Constants::MODULE . '_share_form';
    return $form[$shareForm];
  }
 
  /*--------------------------------------------------------------------
   *
   * Execute.
   *
   *--------------------------------------------------------------------*/
 
  /**
   * {@inheritdoc}
   */
  public function execute() {
    // Always a selection that is a root item.
    $selectionIds = $this->getSelectionIds();
    $item = FolderShare::load(reset($selectionIds));
 
    try {
      $item->share($this->configuration['grants']);
    }
    catch (\Exception $e) {
      \Drupal::messenger()->addMessage($e->getMessage(), 'error');
    }
 
    // Flush the render cache because sharing may have changed what
    // content is viewable throughout the folder tree under this root.
    Cache::invalidateTags(['rendered']);
 
    if (Settings::getCommandNormalCompletionReportEnable() === TRUE) {
      \Drupal::messenger()->addMessage(
        t("The shared access configuration has been updated."),
        'status');
    }
  }
 
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc