SU-SWS/stanford_profile_helper

View on GitHub
modules/stanford_intranet/stanford_intranet.module

Summary

Maintainability
Test Coverage
<?php

/**
 * @file
 * stanford_intranet.module
 */

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\file\FileInterface;
use Drupal\node\NodeInterface;
use Drupal\paragraphs\ParagraphInterface;
use Drupal\stanford_intranet\Plugin\Field\FieldType\EntityAccessFieldType;
use Drupal\user\RoleInterface;
use Symfony\Component\Finder\Finder;

/**
 * Implements hook_page_attachments().
 */
function stanford_intranet_page_attachments(array &$attachments) {
  if (\Drupal::state()->get('stanford_intranet', FALSE) && !\Drupal::service('router.admin_context')->isAdminRoute()) {
    $attachments['#attached']['library'][] = "stanford_intranet/intranet";
  }
}

/**
 * Implements hook_file_download().
 */
function stanford_intranet_file_download($uri) {
  // When viewing images that were converted to PNG files, the Image Effects
  // module appends the `.png` extension onto the end of the existing uri.
  // When a user visits a page with an image that was converted to PNG, Drupal
  // core throws an access-denied error because it's unable to find the original
  // image because of the extension changing. We simply need to remove the
  // ending png extension and then pass it back to the core hook.
  // @see \Drupal\image\Controller\ImageStyleDownloadController::deliver().
  // @see file_file_download().
  if (preg_match('/.jp[e]?g.png$/', $uri) && StreamWrapperManager::getScheme($uri) == 'private') {
    return \Drupal::moduleHandler()->invokeAll('file_download', [str_replace('.png', '', $uri)]);
  }

  $file_repository = \Drupal::service('file.repository');
  $file = $file_repository->loadByUri($uri);
  if (!$file) {
    return;
  }

  $usage_list = \Drupal::service('file.usage')->listUsage($file);
  // Allow icon files to be viewed. All other files on the system are referenced
  // via media entities, so they will go through normal access checks. This
  // allows media library icons, paragraph type icons, etc to be viewed and
  // downloaded.
  if (!isset($usage_list['file']['media'])) {
    return file_get_content_headers($file);
  }
}

/**
 * Implements hook_ENTITY_TYPE_access().
 */
function stanford_intranet_file_access(FileInterface $file, $operation, AccountInterface $account) {
  $usage = \Drupal::service('file.usage')->listUsage($file);

  // Allow the user to "download" the file if it meets the conditions. This
  // allows images that are saved on config pages to be viewed by authenticated
  // users. Such fields like the logo field.
  $allowed = \Drupal::state()->get('stanford_intranet', FALSE) &&
    $account->isAuthenticated() &&
    isset($usage['file']['config_pages']) &&
    (in_array($operation, ['download', 'view']));
  return AccessResult::allowedIf($allowed);
}

/**
 * Implements hook_library_info_build().
 */
function stanford_intranet_library_info_build() {
  $libraries = [];
  $module_path = \Drupal::service('extension.list.module')->getPath('stanford_intranet');

  // Find all css files in the dist/css directory.
  $finder = new Finder();
  $finder->in("$module_path/dist/css")
    ->files()
    ->name('/.css$/');

  foreach ($finder->getIterator() as $file) {
    $local_path = str_replace("$module_path/", '', $file->getPath());

    $path_parts = explode('/', $local_path);
    // Remove `dist` and `css` parts.
    unset($path_parts[0], $path_parts[1]);

    // This is the directory the file lives in.
    $library_level = reset($path_parts);
    $bucket = next($path_parts);
    $lib = basename($file->getFilename(), '.css');

    // Build the library definition.
    $libraries[trim("$bucket.$lib", '. ')] = [
      'css' => [
        $library_level => [
          "$local_path/{$file->getFileName()}" => [],
        ],
      ],
    ];
  }

  return $libraries;
}

/**
 * Implements hook_entity_create_access().
 */
function stanford_intranet_entity_create_access(AccountInterface $account, array $context, $entity_bundle) {
  // Block access to uploading files on the intranet. Leave the door open for
  // the user 1 account though.
  if (
    $context['entity_type_id'] == 'media' &&
    $entity_bundle == 'file' &&
    $account->id() != 1 &&
    \Drupal::state()->get('stanford_intranet', FALSE) &&
    !\Drupal::state()->get('stanford_intranet.allow_file_uploads', FALSE)
  ) {
    return AccessResult::forbidden();
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_entity_access().
 */
function stanford_intranet_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {

  // Paragraphs inherit their access from the parents they live on, so we can
  // ignore them.
  // Check for the role because doing `$account->isAuthenticated() only checks
  // for the uid > 0. This doesn't work for search_api functionality, so just
  // check for the role instead.
  if (
    !in_array(RoleInterface::AUTHENTICATED_ID, $account->getRoles()) &&
    \Drupal::state()->get('stanford_intranet', FALSE) &&
    !($entity instanceof ParagraphInterface)
  ) {
    if ($entity->getEntityTypeId() == 'block') {
      $default = ['system_main_block', 'help', 'system_messages_block'];
      $allowed_blocks = \Drupal::config('stanford_intranet.settings')
        ->get('public_blocks') ?: $default;

      if (
        in_array($entity->getPluginId(), $allowed_blocks) ||
        in_array($entity->id(), $allowed_blocks)
      ) {
        return AccessResult::neutral();
      }
    }
    // Prevent all access to non-authenticated users.
    return AccessResult::forbidden();
  }
  return AccessResult::neutral();
}

/**
 * Implements hook_node_access_records().
 */
function stanford_intranet_node_access_records(NodeInterface $node) {
  $grants = [];

  // If intranet is disabled or the node is not published, we don't want to
  // adjust any access.
  if (
    !$node->isPublished() ||
    !\Drupal::state()->get('stanford_intranet', FALSE) ||
    !$node->hasField(EntityAccessFieldType::FIELD_NAME)
  ) {
    return $grants;
  }

  $rids = \Drupal::state()->get('stanford_intranet.rids');
  $node_field_values = $node->get(EntityAccessFieldType::FIELD_NAME)
    ->getValue();

  // If the node has no access settings configured, we can say that it is
  // visible to all authenticated users.
  if (empty($node_field_values)) {
    $node_field_values = [['role' => 'authenticated', 'access' => ['view']]];
  }

  foreach ($node_field_values as $value) {
    $grant = [
      'realm' => 'stanford_intranet_roles',
      'gid' => $rids[$value['role']],
      'grant_view' => 0,
      'grant_update' => 0,
      'grant_delete' => 0,
    ];

    foreach ($value['access'] as $access) {
      $grant["grant_$access"] = 1;
    }
    $grants[] = $grant;
  }
  $grants[] = [
    'realm' => 'stanford_intranet_author',
    'gid' => $node->getOwner()->id(),
    'grant_view' => 1,
    'grant_update' => 1,
    'grant_delete' => 1,
  ];

  return $grants;
}

/**
 * Implements hook_node_grants().
 */
function stanford_intranet_node_grants(AccountInterface $account, $op) {
  $rids = \Drupal::state()->get('stanford_intranet.rids', []);

  $gids = [];
  $roles = $account->getRoles();
  foreach ($roles as $role_name) {
    if (isset($rids[$role_name])) {
      $gids[] = $rids[$role_name];
    }
  }

  return [
    'stanford_intranet_author' => [$account->id()],
    'stanford_intranet_roles' => $gids,
  ];
}

/**
 * Implements hook_ENTITY_TYPE_insert().
 */
function stanford_intranet_user_role_insert(RoleInterface $role) {
  $state = \Drupal::state()->get('stanford_intranet.rids', []);
  $state = array_flip($state);
  $roles = \Drupal::entityTypeManager()
    ->getStorage('user_role')
    ->loadMultiple();

  foreach (array_keys($roles) as $role_id) {
    if (!in_array($role_id, $state)) {
      $state[] = $role_id;
    }
  }

  \Drupal::state()->set('stanford_intranet.rids', array_flip($state));
}

/**
 * Implements hook_ENTITY_TYPE_predelete().
 */
function stanford_intranet_user_role_predelete(RoleInterface $role){
  $state = \Drupal::state()->get('stanford_intranet.rids', []);
  unset($state[$role->id()]);
  \Drupal::state()->set('stanford_intranet.rids', $state);
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function stanford_intranet_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if (isset($form[EntityAccessFieldType::FIELD_NAME])) {
    $form[EntityAccessFieldType::FIELD_NAME]['#group'] = 'revision_information';
  }
}