modules/stanford_intranet/stanford_intranet.module
<?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';
}
}