Skip to content

Commit

Permalink
Merge pull request #238 from localgovdrupal/2.x
Browse files Browse the repository at this point in the history
2.13.6
  • Loading branch information
finnlewis authored Aug 20, 2024
2 parents 43a8531 + fdd7570 commit 9fc2c7d
Show file tree
Hide file tree
Showing 18 changed files with 468 additions and 3 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
# LocalGov Drupal core functionality

LocalGov Drupal Core module, for helper functions and core dependencies.

## Default blocks
This module contains a mechanism that will place default blocks into your site's
active theme when other localgov modules are installed. This is intended to
reduce the work that site owners need to do when installing new features
provided by localgov modules. If you don't want this to happen, you can turn it
off by adding this to your site's settings.php file:

```php
$config['localgov_core.settings']['install_default_blocks'] = FALSE;
```

If you're a module maintainer and would like to use this feature, create a file
in your module at config/localgov/block.description.yml. The description part of
the filename can be anything you like.

In that file, place the exported config yaml for a single block, and remove the
following keys:
* uuid
* id
* theme

The default block installer will read the file, and create an instance of the
block in the current active theme, along with localgov_base and
localgov_scarfolk, if they exist and are enabled. An id for each instance will
be generated from the combination of theme and block plugin name.

Using this feature lets your blocks appear automatically in the right place in
existing localgov sites with custom themes. It also saves you having to manage
multiple block config files for localgov_base and localgov_scarfolk.
7 changes: 7 additions & 0 deletions config/schema/localgov_core_settings.schema.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
localgov_core.settings:
type: config_object
label: 'LocalGov Core settings'
mapping:
install_default_blocks:
type: boolean
label: 'Install default blocks when modules provide them'
37 changes: 37 additions & 0 deletions localgov_core.module
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* LocalGovDrupal Core module file.
*/

use Drupal\Core\Installer\InstallerKernel;

/**
* Implements hook_theme().
*/
Expand Down Expand Up @@ -35,3 +37,38 @@ function localgov_core_template_preprocess_default_variables_alter(&$variables)
$variables['localgov_base_remove_js'] = TRUE;
}
}

/**
* Implements hook_modules_installed().
*
* This installs default blocks for modules when they're enabled on an existing
* site. (IE, not in the installer.)
*/
function localgov_core_modules_installed(array $modules): void {
// If we're in the installer, do nothing.
if (InstallerKernel::installationAttempted()) {
return;
}

/** @var \Drupal\localgov_core\Service\DefaultBlockInstaller $defaultBlockInstaller */
$defaultBlockInstaller = \Drupal::service('localgov_core.default_block_installer');
$defaultBlockInstaller->install($modules);
}

/**
* Implements hook_localgov_post_install().
*
* This installs default blocks as part of the installation of a new LocalGov
* site. All LocalGov modules (IE ones whose name starts with 'localgov_') will
* have default blocks that they define installed.
*/
function localgov_core_localgov_post_install(): void {
$moduleList = \Drupal::moduleHandler()->getModuleList();
$localgovModules = array_filter(array_keys($moduleList), function ($moduleName) {
return str_starts_with($moduleName, 'localgov_');
});

/** @var \Drupal\localgov_core\Service\DefaultBlockInstaller $defaultBlockInstaller */
$defaultBlockInstaller = \Drupal::service('localgov_core.default_block_installer');
$defaultBlockInstaller->install($localgovModules);
}
4 changes: 4 additions & 0 deletions localgov_core.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
localgov_core.default_block_installer:
class: Drupal\localgov_core\Service\DefaultBlockInstaller
arguments: ['@config.factory', '@entity_type.manager', '@file_system', '@module_handler', '@theme_handler', '@theme.manager']
7 changes: 7 additions & 0 deletions modules/localgov_admin_role/localgov_admin_role.info.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: LocalGov Admin Role
description: Creates an 'admin' role and assigns it all permissions.
package: LocalGov Drupal
type: module
core_version_requirement: ^10 || ^11
dependencies:
- localgov_core:localgov_roles
26 changes: 26 additions & 0 deletions modules/localgov_admin_role/localgov_admin_role.install
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/**
* @file
* Install, update and uninstall functions for the localgov_admin_role module.
*/

use Drupal\localgov_roles\RolesHelper;
use Drupal\user\Entity\Role;

/**
* Implements hook_install().
*/
function localgov_admin_role_install() {
// Check if the 'localgov_admin_role' role exists.
$admin_role = Role::load(RolesHelper::ADMIN_ROLE);
if (!$admin_role) {
// If the role doesn't exist, create it.
$admin_role = Role::create([
'id' => 'localgov_admin',
'label' => 'LocalGov Admin',
'is_admin' => TRUE,
]);
$admin_role->save();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#revision-overview-form {
position: relative;
}

#revision-overview-form [data-drupal-selector="edit-submit-top"] {
position: sticky;
top: var(--drupal-displace-offset-top);
z-index: 600;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
gin-layout-paragraphs:
js:
js/gin-layout-paragraphs.js: {}

revisions-page:
css:
theme:
css/revisions-page.css: {}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ function localgov_admin_theme_improvements_layout_paragraph_element_form_alter(a
}
}

/**
* Implements hook_form_FORM_ID_alter().
*/
function localgov_admin_theme_improvements_form_revision_overview_form_alter(&$form, FormStateInterface $form_state, $form_id) {
if ($form_id === 'revision_overview_form') {
$form['#attached']['library'][] = 'localgov_admin_theme_improvements/revisions-page';
}
}

/**
* Implements hook_menu_local_tasks_alter().
*/
Expand Down
8 changes: 5 additions & 3 deletions modules/localgov_roles/localgov_roles.module
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ use Drupal\localgov_roles\RolesHelper;
/**
* Implements hook_modules_installed().
*/
function localgov_roles_modules_installed($modules) {
foreach ($modules as $module) {
RolesHelper::assignModuleRoles($module);
function localgov_roles_modules_installed($modules, $is_syncing) {
if (!$is_syncing) {
foreach ($modules as $module) {
RolesHelper::assignModuleRoles($module);
}
}
}
5 changes: 5 additions & 0 deletions modules/localgov_roles/src/RolesHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
*/
class RolesHelper {

/**
* Admin role machine name.
*/
const ADMIN_ROLE = 'localgov_admin';

/**
* Editor role machine name.
*/
Expand Down
185 changes: 185 additions & 0 deletions src/Service/DefaultBlockInstaller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
<?php

namespace Drupal\localgov_core\Service;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
use Symfony\Component\Yaml\Yaml;

/**
* Service to install default blocks.
*/
class DefaultBlockInstaller {

/**
* Array of regions in each theme.
*
* @var array
*/
protected $themeRegions = [];

/**
* Constructor.
*/
public function __construct(
protected ConfigFactoryInterface $configFactory,
protected EntityTypeManagerInterface $entityTypeManager,
protected FileSystemInterface $fileSystem,
protected ModuleHandlerInterface $moduleHandler,
protected ThemeHandlerInterface $themeHandler,
protected ThemeManagerInterface $themeManager,
) {
}

/**
* Read the yaml files provided by modules.
*/
protected function blockDefinitions(string $module): array {

$modulePath = $this->moduleHandler->getModule($module)->getPath();
$moduleBlockDefinitionsPath = $modulePath . '/config/localgov';
$blocks = [];

if (is_dir($moduleBlockDefinitionsPath)) {
$files = $this->fileSystem->scanDirectory($moduleBlockDefinitionsPath, '/block\..+\.yml$/');
foreach ($files as $file) {
$blocks[] = Yaml::parseFile($moduleBlockDefinitionsPath . '/' . $file->filename);
}
}

return $blocks;
}

/**
* The themes we'll be installing blocks into.
*/
protected function targetThemes(): array {

$themes = ['localgov_base', 'localgov_scarfolk'];

$activeTheme = $this->getActiveThemeName();
if ($activeTheme && !in_array($activeTheme, $themes)) {
$themes[] = $activeTheme;
}

// Don't try to use themes that don't exist.
foreach ($themes as $i => $theme) {
if (!$this->themeHandler->themeExists($theme)) {
unset($themes[$i]);
}
}

return $themes;
}

/**
* Gets the name of the active theme, if there is one.
*/
protected function getActiveThemeName(): ?string {
$activeTheme = $this->themeManager->getActiveTheme()->getName();
if ($activeTheme === 'core') {
// 'core' is what Drupal returns when there's no themes available.
// See \Drupal\Core\Theme\ThemeInitialization::getActiveThemeByName().
// This mainly happens in the installer.
return NULL;
}
return $activeTheme;
}

/**
* Installs the default blocks for the given modules.
*
* If the site is configured not to allow default blocks to be installed, this
* method will do nothing.
*/
public function install(array $modules): void {

// If localgov_core.settings.install_default_blocks is set to FALSE, don't
// install default blocks. This lets site owners opt out if desired.
$config = $this->configFactory->get('localgov_core.settings');
if ($config && ($config->get('install_default_blocks') === FALSE)) {
return;
}

foreach ($modules as $module) {
$this->installForModule($module);
}
}

/**
* Installs the default blocks for the given module.
*/
protected function installForModule(string $module): void {

$blocks = $this->blockDefinitions($module);

// Loop over every theme and block definition, so we set up all the blocks
// in all the relevant themes.
foreach ($this->targetThemes() as $theme) {
foreach ($blocks as $block) {

// Verify that the theme we're using has the requested region.
if (!$this->themeHasRegion($theme, $block['region'])) {
continue;
}

$block['id'] = $this->sanitiseId($theme . '_' . $block['plugin']);
$block['theme'] = $theme;

$this->entityTypeManager
->getStorage('block')
->create($block)
->save();
}
}
}

/**
* Replace characters that aren't allowed in config IDs.
*
* This is partly based on
* \Drupal\Core\Block\BlockPluginTrait::getMachineNameSuggestion().
*/
protected function sanitiseId(string $id): string {

// Shift to lower case.
$id = mb_strtolower($id);

// Limit to alphanumeric chars, dot and underscore.
$id = preg_replace('@[^a-z0-9_.]+@', '_', $id);

// Remove non-alphanumeric chars from the beginning and end of the id.
$id = preg_replace('@^([^a-z0-9]+)|([^a-z0-9]+)$@', '', $id);

return $id;
}

/**
* Does the given theme have the given region?
*/
protected function themeHasRegion(string $theme, string $region): bool {
return in_array($region, $this->themeRegions($theme));
}

/**
* Gets the regions for the given theme.
*/
protected function themeRegions(string $theme): array {
if (!isset($this->themeRegions[$theme])) {
$themeInfo = $this->themeHandler->getTheme($theme);
if (empty($themeInfo)) {
$regions = [];
}
else {
$regions = array_keys($themeInfo->info['regions']);
}
$this->themeRegions[$theme] = $regions;
}
return $this->themeRegions[$theme];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugin: 'localgov_core_test_block'
region: bad_region
status: true
weight: 1
settings:
label: 'Block in a bad region.'
visibility: { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugin: 'localgov_core_test_block'
region: content_top
status: true
weight: 1
settings:
label: 'Block in a good region.'
visibility: { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: 'Default blocks test'
type: module
package: Testing
core_version_requirement: ^9 || ^10
description: Testing module for default blocks.
Loading

0 comments on commit 9fc2c7d

Please sign in to comment.