Rename and re-organise custom modules
- Rename `opd_talks` to `opdavies_talks` - Rename `custom` to `opdavies_blog`
This commit is contained in:
parent
e4e898f22c
commit
9b1a8fb3be
53 changed files with 125 additions and 116 deletions
|
|
@ -0,0 +1,6 @@
|
|||
opdavies_talks.settings:
|
||||
type: config_object
|
||||
label: 'Talks module configuration'
|
||||
mapping:
|
||||
zapier_post_tweet_url:
|
||||
type: string
|
||||
11
web/modules/custom/opdavies_blog/drush.services.yml
Normal file
11
web/modules/custom/opdavies_blog/drush.services.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
services:
|
||||
Drupal\opdavies_blog\Command\ExportBodyValuesForThemePurgingCommand:
|
||||
arguments: ['@database']
|
||||
autowire: true
|
||||
tags:
|
||||
- { name: drush.command }
|
||||
|
||||
Drupal\opdavies_blog\Command\FormatTagNamesCommand:
|
||||
autowire: true
|
||||
tags:
|
||||
- { name: drush.command }
|
||||
20
web/modules/custom/opdavies_blog/hooks/entity_type/build.inc
Normal file
20
web/modules/custom/opdavies_blog/hooks/entity_type/build.inc
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Entity type build hooks.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Drupal\discoverable_entity_bundle_classes\Storage\Node\NodeStorage;
|
||||
|
||||
/**
|
||||
* Implements hook_entity_type_build().
|
||||
*/
|
||||
function opdavies_blog_entity_type_build(array &$entityTypes): void {
|
||||
/** @var \Drupal\Core\Entity\EntityTypeInterface[] $entityTypes */
|
||||
if (isset($entityTypes['node'])) {
|
||||
$entityTypes['node']->setStorageClass(NodeStorage::class);
|
||||
}
|
||||
}
|
||||
28
web/modules/custom/opdavies_blog/hooks/node_links/alter.inc
Normal file
28
web/modules/custom/opdavies_blog/hooks/node_links/alter.inc
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Node links alter hooks.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_node_links_alter().
|
||||
*/
|
||||
function opdavies_blog_node_links_alter(array &$links, NodeInterface $node): void {
|
||||
if (!method_exists($node, 'getExternalLink')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($link = $node->getExternalLink()) {
|
||||
$links['node']['#links']['node-readmore']['url'] = Url::fromUri($link['uri']);
|
||||
$links['node']['#links']['node-readmore']['title'] = t('Read more<span class="visually-hidden"> about @title</span> (<span class="visually-hidden">on </span>@domain)', [
|
||||
'@domain' => $link['title'],
|
||||
'@title' => $node->label(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
18
web/modules/custom/opdavies_blog/hooks/preprocess/block.inc
Normal file
18
web/modules/custom/opdavies_blog/hooks/preprocess/block.inc
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Block preprocess hooks.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK().
|
||||
*/
|
||||
function opdavies_blog_preprocess_block(array &$variables): void {
|
||||
// Add the 'markup' class to blocks.
|
||||
if (in_array($variables['plugin_id'], ['views_block:featured_blog_posts-block_1'])) {
|
||||
$variables['attributes']['class'][] = 'markup';
|
||||
}
|
||||
}
|
||||
21
web/modules/custom/opdavies_blog/hooks/preprocess/node.inc
Normal file
21
web/modules/custom/opdavies_blog/hooks/preprocess/node.inc
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Node preprocess functions.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_HOOK().
|
||||
*/
|
||||
function opdavies_blog_preprocess_node(array &$variables): void {
|
||||
if (!method_exists($variables['node'], 'getExternalLink')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($link = $variables['node']->getExternalLink()) {
|
||||
$variables['url'] = $link['uri'];
|
||||
}
|
||||
}
|
||||
9
web/modules/custom/opdavies_blog/opdavies_blog.info.yml
Normal file
9
web/modules/custom/opdavies_blog/opdavies_blog.info.yml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
name: Oliver Davies blog
|
||||
type: module
|
||||
core_version_requirement: ^8 || ^9
|
||||
package: Custom
|
||||
dependencies:
|
||||
- drupal:node
|
||||
- discoverable_entity_bundle_classes:discoverable_entity_bundle_classes
|
||||
- hook_event_dispatcher:hook_event_dispatcher
|
||||
- paragraphs:paragraphs
|
||||
17
web/modules/custom/opdavies_blog/opdavies_blog.install
Normal file
17
web/modules/custom/opdavies_blog/opdavies_blog.install
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Drupal\opdavies_blog\Repository\PostRepository;
|
||||
|
||||
/**
|
||||
* Mark existing blog posts as sent to social media.
|
||||
*/
|
||||
function opdavies_blog_update_8001(): void {
|
||||
$posts = \Drupal::service(PostRepository::class)->getAll();
|
||||
|
||||
foreach ($posts as $post) {
|
||||
$post->set('field_sent_to_social_media', TRUE);
|
||||
$post->save();
|
||||
}
|
||||
}
|
||||
18
web/modules/custom/opdavies_blog/opdavies_blog.module
Normal file
18
web/modules/custom/opdavies_blog/opdavies_blog.module
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Custom module.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
$finder = Finder::create()
|
||||
->in(__DIR__ . DIRECTORY_SEPARATOR . 'hooks')
|
||||
->name('/.[php|inc]$/');
|
||||
|
||||
foreach ($finder as $file) {
|
||||
include $file->getPathname();
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
services:
|
||||
Drupal\opdavies_blog\EventSubscriber\PushBlogPostToSocialMedia:
|
||||
tags:
|
||||
- { name: event_subscriber }
|
||||
|
||||
Drupal\opdavies_blog\Repository\PostRepository:
|
||||
autowire: true
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\opdavies_blog\Command;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
final class ExportBodyValuesForThemePurgingCommand {
|
||||
|
||||
private static array $tableNames = [
|
||||
'block_content__body',
|
||||
'node__body',
|
||||
];
|
||||
|
||||
private string $filename = 'body-field-values.txt';
|
||||
|
||||
private Connection $database;
|
||||
|
||||
public function __construct(Connection $database) {
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drush command to export body field values into a file.
|
||||
*
|
||||
* @command opdavies:export-body-values-for-theme-purging
|
||||
*/
|
||||
public function handle(): void {
|
||||
$values = Collection::make(self::$tableNames)
|
||||
->flatMap(fn(string $tableName) => $this->getValuesFromTable($tableName))
|
||||
->implode(PHP_EOL);
|
||||
|
||||
file_put_contents($this->getFilePath(), $values);
|
||||
}
|
||||
|
||||
private function getFilePath(): string {
|
||||
return drupal_get_path('theme', 'opdavies') . DIRECTORY_SEPARATOR . $this->filename;
|
||||
}
|
||||
|
||||
private function getValuesFromTable(string $tableName): array {
|
||||
return $this->database->select($tableName)
|
||||
->fields($tableName, ['body_value'])
|
||||
->execute()
|
||||
->fetchCol();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\opdavies_blog\Command;
|
||||
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drush\Commands\DrushCommands;
|
||||
|
||||
final class FormatTagNamesCommand extends DrushCommands {
|
||||
|
||||
/**
|
||||
* A lookup table for new name overrides.
|
||||
*
|
||||
* @var array
|
||||
* An associative array, keyed by the original tag name. The value is either
|
||||
* an overridden tag name or FALSE if the tag name is not to be changed.
|
||||
*/
|
||||
private static $tagNames = [
|
||||
'accessible-bristol' => 'Accessible Bristol',
|
||||
'admin:hover' => FALSE,
|
||||
'aria' => 'ARIA',
|
||||
'cck' => 'CCK',
|
||||
'centos' => 'CentOS',
|
||||
'css' => 'CSS',
|
||||
'dcbristol' => FALSE,
|
||||
'ddev' => 'DDEV',
|
||||
'drupal-association' => 'Drupal Association',
|
||||
'drupal-bristol' => 'Drupal Bristol',
|
||||
'drupal-commerce' => 'Drupal Commerce',
|
||||
'drupal-planet' => 'Drupal Planet',
|
||||
'drupal-vm' => 'Drupal VM',
|
||||
'drupal-vm-generator' => 'Drupal VM Generator',
|
||||
'drupalcamp' => 'DrupalCamp',
|
||||
'drupalcamp-bristol' => 'DrupalCamp Bristol',
|
||||
'drupalcamp-london' => 'DrupalCamp London',
|
||||
'drupalcamp-north' => 'DrupalCamp North',
|
||||
'drupalcon' => 'DrupalCon',
|
||||
'entity-api' => 'Entity API',
|
||||
'fancy-slide' => 'Fancy Slide',
|
||||
'field-collection' => 'Field Collection',
|
||||
'filefield' => 'FileField',
|
||||
'form-api' => 'Form API',
|
||||
'git-flow' => 'Git Flow',
|
||||
'github' => 'GitHub',
|
||||
'github-actions' => 'GitHub Actions',
|
||||
'illuminate-collections' => 'Illuminate Collections',
|
||||
'image-caption' => 'Image Caption',
|
||||
'imagecache' => 'ImageCache',
|
||||
'imagefield' => 'ImageField',
|
||||
'imagefield-import' => 'ImageField Import',
|
||||
'javascript' => 'JavaScript',
|
||||
'laravel-collections' => 'Laravel Collections',
|
||||
'laravel-mix' => 'Laravel Mix',
|
||||
'linux-journal' => 'Linux Journal',
|
||||
'mac-os-x' => 'macOS',
|
||||
'mamp' => 'MAMP',
|
||||
'mod_rewrite' => FALSE,
|
||||
'npm' => FALSE,
|
||||
'oliverdavies.co.uk' => FALSE,
|
||||
'php' => 'PHP',
|
||||
'php-south-wales' => 'PHP South Wales',
|
||||
'phpstorm' => 'PhpStorm',
|
||||
'phpsw' => 'PHPSW',
|
||||
'phpunit' => 'PHPUnit',
|
||||
'postcss' => 'PostCSS',
|
||||
'psr' => 'PSR',
|
||||
'regular-expression' => 'Regular expressions',
|
||||
'sequel-pro' => 'Sequel Pro',
|
||||
'settings.php' => FALSE,
|
||||
'sql' => 'SQL',
|
||||
'ssh' => 'SSH',
|
||||
'sublime-text' => 'Sublime Text',
|
||||
'svn' => 'SVN',
|
||||
'swdug' => 'SWDUG',
|
||||
'symfonylive' => 'SymfonyLive',
|
||||
'tailwind-css' => 'Tailwind CSS',
|
||||
'tdd' => 'TDD',
|
||||
'test-driven-drupal' => 'Test Driven Drupal',
|
||||
'views-attach' => 'Views Attach',
|
||||
'virtualbox' => 'VirtualBox',
|
||||
'vuejs' => 'VueJS',
|
||||
'virtualhostx' => 'VirtualHostX',
|
||||
];
|
||||
|
||||
/**
|
||||
* The taxonomy term storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
private $termStorage;
|
||||
|
||||
public function __construct(EntityTypeManagerInterface $entityTypeManager) {
|
||||
parent::__construct();
|
||||
|
||||
$this->termStorage = $entityTypeManager->getStorage('taxonomy_term');
|
||||
}
|
||||
|
||||
/**
|
||||
* Drush command for updating legacy tag names.
|
||||
*
|
||||
* @command opdavies:update-tag-names
|
||||
*/
|
||||
public function updateTagNames(): void {
|
||||
foreach ($this->getTags() as $tag) {
|
||||
$name = $tag->label();
|
||||
$newName = $this->getNewTagName($name);
|
||||
|
||||
if ($newName === NULL) {
|
||||
$this->writeln(sprintf('Skipping %s.', $name));
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->writeln(sprintf('Updating %s to %s.', $name, $newName));
|
||||
$tag->name = $newName;
|
||||
$tag->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function getTags(): array {
|
||||
return $this->termStorage->loadByProperties([
|
||||
'vid' => 'tags',
|
||||
]);
|
||||
}
|
||||
|
||||
private function getNewTagName(string $tagName): ?string {
|
||||
if (!array_key_exists($tagName, static::$tagNames)) {
|
||||
return str_replace('-', ' ', ucfirst($tagName));
|
||||
}
|
||||
|
||||
if (static::$tagNames[$tagName] === FALSE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return static::$tagNames[$tagName];
|
||||
}
|
||||
|
||||
}
|
||||
47
web/modules/custom/opdavies_blog/src/Entity/Node/Post.php
Normal file
47
web/modules/custom/opdavies_blog/src/Entity/Node/Post.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\opdavies_blog\Entity\Node;
|
||||
|
||||
use Drupal\discoverable_entity_bundle_classes\ContentEntityBundleInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Defines an blog post node class.
|
||||
*
|
||||
* @ContentEntityBundleClass(
|
||||
* label = @Translation("Blog post"),
|
||||
* entity_type = "node",
|
||||
* bundle = "post"
|
||||
* );
|
||||
*/
|
||||
class Post extends Node implements ContentEntityBundleInterface {
|
||||
|
||||
public function getExternalLink(): ?array {
|
||||
return ($link = $this->get('field_external_link')->get(0))
|
||||
? $link->getValue()
|
||||
: NULL;
|
||||
}
|
||||
|
||||
public function hasBeenSentToSocialMedia(): bool {
|
||||
return (bool) $this->get('field_sent_to_social_media')->getString();
|
||||
}
|
||||
|
||||
public function hasTweet(): bool {
|
||||
return (bool) $this->get('field_has_tweet')->getString();
|
||||
}
|
||||
|
||||
public function isExternalPost(): bool {
|
||||
return (bool) $this->getExternalLink();
|
||||
}
|
||||
|
||||
public function toTweet(): string {
|
||||
// TODO: Add tags.
|
||||
|
||||
$parts = [$this->label(), $this->url('canonical', ['absolute' => TRUE])];
|
||||
|
||||
return implode(PHP_EOL . PHP_EOL, $parts);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\opdavies_blog\EventSubscriber;
|
||||
|
||||
use Drupal\hook_event_dispatcher\Event\Entity\BaseEntityEvent;
|
||||
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
|
||||
use Drupal\opdavies_blog\Entity\Node\Post;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
final class PushBlogPostToSocialMedia implements EventSubscriberInterface {
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getSubscribedEvents() {
|
||||
return [
|
||||
HookEventDispatcherInterface::ENTITY_PRE_SAVE => 'onEntityPreSave',
|
||||
];
|
||||
}
|
||||
|
||||
public function onEntityPresave(BaseEntityEvent $event): void {
|
||||
$entity = $event->getEntity();
|
||||
|
||||
if ($entity->getEntityTypeId() != 'node') {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Post $entity */
|
||||
if ($entity->bundle() != 'post') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$entity->isPublished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this post has already been sent to social media, do not send it again.
|
||||
if ($entity->hasBeenSentToSocialMedia()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($entity->isExternalPost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = \Drupal::configFactory()->get('opdavies_talks.config')
|
||||
->get('zapier_post_tweet_url');
|
||||
|
||||
\Drupal::httpClient()->post($url, [
|
||||
'form_params' => [
|
||||
'message' => $entity->toTweet(),
|
||||
],
|
||||
]);
|
||||
|
||||
$entity->set('field_sent_to_social_media', TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\opdavies_blog\Repository;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
final class PostRepository {
|
||||
|
||||
private EntityStorageInterface $nodeStorage;
|
||||
|
||||
public function __construct(EntityTypeManagerInterface $entityTypeManager) {
|
||||
$this->nodeStorage = $entityTypeManager->getStorage('node');
|
||||
}
|
||||
|
||||
public function getAll(): Collection {
|
||||
return new Collection(
|
||||
$this->nodeStorage->loadByProperties([
|
||||
'type' => 'post',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_has_tweet
|
||||
- node.type.post
|
||||
id: node.post.field_has_tweet
|
||||
field_name: field_has_tweet
|
||||
entity_type: node
|
||||
bundle: post
|
||||
label: 'Has tweet'
|
||||
description: 'Check to include Twitter''s widget.js script for this page.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value:
|
||||
-
|
||||
value: 0
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
on_label: 'Yes'
|
||||
off_label: 'No'
|
||||
field_type: boolean
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
id: node.field_has_tweet
|
||||
field_name: field_has_tweet
|
||||
entity_type: node
|
||||
type: boolean
|
||||
settings: { }
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
third_party_settings: { }
|
||||
name: 'Blog post'
|
||||
type: post
|
||||
description: 'A single blog post.'
|
||||
help: ''
|
||||
new_revision: true
|
||||
preview_mode: 1
|
||||
display_submitted: true
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
name: Oliver Davies Posts Test
|
||||
type: module
|
||||
core_version_requirement: ^8 || ^9
|
||||
hidden: true
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\custom\Kernel\Entity\Node;
|
||||
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\opdavies_blog\Entity\Node\Post;
|
||||
|
||||
final class PostTest extends EntityKernelTestBase {
|
||||
|
||||
public static $modules = [
|
||||
// Core.
|
||||
'node',
|
||||
|
||||
// Contrib.
|
||||
'discoverable_entity_bundle_classes',
|
||||
|
||||
// Custom.
|
||||
'opdavies_blog',
|
||||
'opdavies_blog_test',
|
||||
];
|
||||
|
||||
/** @test */
|
||||
public function it_can_determine_if_a_post_contains_a_tweet(): void {
|
||||
/** @var Post $post */
|
||||
$post = Node::create(['type' => 'post']);
|
||||
$this->assertFalse($post->hasTweet());
|
||||
|
||||
/** @var Post $post */
|
||||
$post = Node::create([
|
||||
'field_has_tweet' => TRUE,
|
||||
'type' => 'post',
|
||||
]);
|
||||
$this->assertTrue($post->hasTweet());
|
||||
}
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(['opdavies_blog_test']);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue