Whole lotta love.
This commit is contained in:
parent
40324ae10b
commit
13971f626c
@ -25,6 +25,7 @@
|
|||||||
"drupal/core-project-message": "^10.3",
|
"drupal/core-project-message": "^10.3",
|
||||||
"drupal/core-recommended": "^10.3",
|
"drupal/core-recommended": "^10.3",
|
||||||
"drupal/devel_entity_updates": "^4.1",
|
"drupal/devel_entity_updates": "^4.1",
|
||||||
|
"drupal/entity_clone": "^2.1@beta",
|
||||||
"drupal/entity_hierarchy": "^3.3",
|
"drupal/entity_hierarchy": "^3.3",
|
||||||
"drupal/field_group": "^3.4",
|
"drupal/field_group": "^3.4",
|
||||||
"drupal/flexslider": "^3.0@alpha",
|
"drupal/flexslider": "^3.0@alpha",
|
||||||
@ -35,6 +36,7 @@
|
|||||||
"drupal/html_formatter": "^2.0",
|
"drupal/html_formatter": "^2.0",
|
||||||
"drupal/jsonapi_permission_access": "1.0.1",
|
"drupal/jsonapi_permission_access": "1.0.1",
|
||||||
"drupal/layout_builder_admin_theme": "^2.0",
|
"drupal/layout_builder_admin_theme": "^2.0",
|
||||||
|
"drupal/layout_builder_block_clone": "^1.3",
|
||||||
"drupal/layout_builder_styles": "^2.0",
|
"drupal/layout_builder_styles": "^2.0",
|
||||||
"drupal/mailsystem": "^4.5",
|
"drupal/mailsystem": "^4.5",
|
||||||
"drupal/migrate_plus": "^6.0",
|
"drupal/migrate_plus": "^6.0",
|
||||||
@ -45,6 +47,7 @@
|
|||||||
"drupal/stage_file_proxy": "^3.1",
|
"drupal/stage_file_proxy": "^3.1",
|
||||||
"drupal/views_bulk_operations": "^4.2",
|
"drupal/views_bulk_operations": "^4.2",
|
||||||
"drupal/views_json_source": "^2.0",
|
"drupal/views_json_source": "^2.0",
|
||||||
|
"drupal/workflow": "^1.8",
|
||||||
"drush/drush": "^12.4",
|
"drush/drush": "^12.4",
|
||||||
"symfony/css-selector": "^6",
|
"symfony/css-selector": "^6",
|
||||||
"symfony/dom-crawler": "^6",
|
"symfony/dom-crawler": "^6",
|
||||||
@ -65,7 +68,8 @@
|
|||||||
"phpstan/extension-installer": true,
|
"phpstan/extension-installer": true,
|
||||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||||
"php-http/discovery": true,
|
"php-http/discovery": true,
|
||||||
"cweagans/composer-patches": true
|
"cweagans/composer-patches": true,
|
||||||
|
"tbachert/spi": true
|
||||||
},
|
},
|
||||||
"sort-packages": true
|
"sort-packages": true
|
||||||
},
|
},
|
||||||
|
|||||||
1424
composer.lock
generated
1424
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +0,0 @@
|
|||||||
name: Auto Alt
|
|
||||||
description: 'AI generated alt text on media items.'
|
|
||||||
type: module
|
|
||||||
package: Media
|
|
||||||
core_version_requirement: ^10 || ^11
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
auto_alt:
|
|
||||||
css:
|
|
||||||
theme:
|
|
||||||
css/styles.css: { }
|
|
||||||
js:
|
|
||||||
js/autoAlt.js: { }
|
|
||||||
dependencies:
|
|
||||||
- core/once
|
|
||||||
- core/drupal
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
function auto_alt_form_alter(&$form, &$form_state, $form_id) {
|
|
||||||
if ($form_id !== 'media_image_edit_form') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$form['auto_alt'] = [
|
|
||||||
'#type' => 'markup',
|
|
||||||
'#markup' => '<span id="autoalt">🪄</span>'
|
|
||||||
];
|
|
||||||
$form['#attached']['library'][] = 'auto_alt/auto_alt';
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
auto_alt.content:
|
|
||||||
path: '/ai/alt-text-generator'
|
|
||||||
defaults:
|
|
||||||
_controller: '\Drupal\auto_alt\Controller\AltTextGenerator::generate'
|
|
||||||
_title: ''
|
|
||||||
requirements:
|
|
||||||
_permission: 'access content'
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
.form-managed-file__meta-items {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#autoalt {
|
|
||||||
position: absolute;
|
|
||||||
top: 46px;
|
|
||||||
right: 3px;
|
|
||||||
padding: 8px;
|
|
||||||
background: 'whitesmoke';
|
|
||||||
transform: translateY(-50%);
|
|
||||||
border-radius: 0 5px 5px 0;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background .2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#autoalt:hover {
|
|
||||||
background: lightgray;
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
(function (Drupal, once) {
|
|
||||||
Drupal.behaviors.autoAltBehavior = {
|
|
||||||
attach: function (context, settings) {
|
|
||||||
once('autoAltBehavior', '#edit-field-media-image-0-alt', context).forEach(function (element) {
|
|
||||||
// Move the wand to where it should be.
|
|
||||||
let wand = context.getElementById("autoalt");
|
|
||||||
let mediaMeta = context.querySelector('.form-managed-file__meta-items');
|
|
||||||
mediaMeta.append(wand);
|
|
||||||
wand.addEventListener('click', generateAltText);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function generateAltText() {
|
|
||||||
let imageUrl = document.querySelector('.image-preview__img-wrapper img').src;
|
|
||||||
const url = "/ai/alt-text-generator?";
|
|
||||||
try {
|
|
||||||
const response = await fetch(url + new URLSearchParams({
|
|
||||||
image: imageUrl,
|
|
||||||
}).toString());
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Response status: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const json = await response.json();
|
|
||||||
let altInput = document.getElementById('edit-field-media-image-0-alt');
|
|
||||||
altInput.value = json;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})(Drupal, once);
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Drupal\auto_alt\Controller;
|
|
||||||
|
|
||||||
use \Drupal\Core\Controller\ControllerBase;
|
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
||||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate Alt Text for an image.
|
|
||||||
*/
|
|
||||||
class AltTextGenerator extends ControllerBase {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a renderable array for a test page.
|
|
||||||
*
|
|
||||||
* return []
|
|
||||||
*/
|
|
||||||
public function generate() {
|
|
||||||
// Get the url to the image from the request.
|
|
||||||
$image_url = \Drupal::request()->get('image');
|
|
||||||
if (!$image_url) {
|
|
||||||
throw new NotFoundHttpException();
|
|
||||||
}
|
|
||||||
$image_contents = file_get_contents($image_url);
|
|
||||||
$encoded_image = base64_encode($image_contents);
|
|
||||||
// Now send this to chat gpt / ai to generate alt text.
|
|
||||||
|
|
||||||
$response = [
|
|
||||||
"This is some alt text for an image.",
|
|
||||||
"A new string for alt text",
|
|
||||||
"Another string about alt text",
|
|
||||||
];
|
|
||||||
$text_to_return = rand(0,2);
|
|
||||||
$json_response = new JsonResponse();
|
|
||||||
$json_response->headers->set('Content-Type', 'application/json');
|
|
||||||
$json_response->setData($response[$text_to_return]);
|
|
||||||
return $json_response;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
24
web/modules/custom/dc_core/css/admin.css
Normal file
24
web/modules/custom/dc_core/css/admin.css
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#block-gin-content .admin-item {
|
||||||
|
margin: 20px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#block-gin-content .admin-item__description {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.draggable .tabledrag-cell {
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity-form-display-form {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity-form-display-form select, .field-plugin-summary {
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-builder-block {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
5
web/modules/custom/dc_core/dc_core.info.yml
Normal file
5
web/modules/custom/dc_core/dc_core.info.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
name: DC Core
|
||||||
|
description: 'Core functionality and overrides.'
|
||||||
|
package: DC Core
|
||||||
|
type: module
|
||||||
|
core_version_requirement: ^10 || ^11
|
||||||
4
web/modules/custom/dc_core/dc_core.libraries.yml
Normal file
4
web/modules/custom/dc_core/dc_core.libraries.yml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
admin_overrides:
|
||||||
|
css:
|
||||||
|
theme:
|
||||||
|
css/admin.css: { }
|
||||||
10
web/modules/custom/dc_core/dc_core.module
Normal file
10
web/modules/custom/dc_core/dc_core.module
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function dc_core_page_attachments(array &$attachments) {
|
||||||
|
if (\Drupal::service('router.admin_context')->isAdminRoute()) {
|
||||||
|
$attachments['#attached']['library'][] = 'dc_core/admin_overrides';
|
||||||
|
}
|
||||||
|
if (\Drupal::routeMatch()->getRouteObject()->getOption('_layout_builder')) {
|
||||||
|
$attachments['#attached']['library'][] = 'dc_core/admin_overrides';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
name: Accordion
|
||||||
|
props:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
accordionType:
|
||||||
|
type: string
|
||||||
|
title: Accordion Type
|
||||||
|
description: 'The type of accordion.'
|
||||||
|
slots:
|
||||||
|
headline:
|
||||||
|
title: Headline
|
||||||
|
required: true
|
||||||
|
description: This is the headline for an accordion.
|
||||||
|
body:
|
||||||
|
title: Body
|
||||||
|
required: true
|
||||||
|
description: This is the body for an accordion.
|
||||||
|
libraryOverrides:
|
||||||
|
dependencies:
|
||||||
|
- core/drupal
|
||||||
|
- core/once
|
||||||
@ -0,0 +1 @@
|
|||||||
|
@charset "UTF-8";.ib-accordion__item{margin:10px 0;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.ib-accordion__headline{background-color:#efefef;padding:20px;cursor:pointer;display:flex;align-items:center}.ib-accordion__headline h1,.ib-accordion__headline h2,.ib-accordion__headline h3,.ib-accordion__headline h4,.ib-accordion__headline h5,.ib-accordion__headline h6{font-size:18px!important;margin:0!important;color:#3272b3;--bs-heading-color:#3272b3}.ib-accordion__headline::before{display:flex;float:left;content:"+";font-size:30px;font-weight:700;justify-content:center;align-items:center;background-color:var(--site-primary);border-radius:100%;width:30px;height:30px;color:#fff;margin-right:10px}.ib-accordion__body{padding:0 20px;max-height:0;overflow:hidden;will-change:max-height;transition:.25s ease-out;align-items:center}.ib-accordion__body>:first-child{margin:20px 0}.ib-accordion__active .ib-accordion__headline::before{content:"−"}
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
(function (Drupal, once) {
|
||||||
|
Drupal.behaviors.accordion = {
|
||||||
|
attach(context) {
|
||||||
|
const accordions = once('accordion', '.ib-accordion__item', context);
|
||||||
|
if (!accordions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const openAccordion = (accordion) => {
|
||||||
|
const content = accordion.querySelector(".ib-accordion__body");
|
||||||
|
accordion.classList.add("ib-accordion__active");
|
||||||
|
content.style.maxHeight = content.scrollHeight + "px";
|
||||||
|
content.setAttribute('aria-expanded', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeAccordion = (accordion) => {
|
||||||
|
const content = accordion.querySelector(".ib-accordion__body");
|
||||||
|
accordion.classList.remove("ib-accordion__active");
|
||||||
|
content.style.maxHeight = null;
|
||||||
|
content.setAttribute('aria-expanded', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
accordions.forEach((accordion) => {
|
||||||
|
const headline = accordion.querySelector(".ib-accordion__headline");
|
||||||
|
const content = accordion.querySelector(".ib-accordion__body");
|
||||||
|
|
||||||
|
headline.onclick = () => {
|
||||||
|
if (content.style.maxHeight) {
|
||||||
|
closeAccordion(accordion);
|
||||||
|
} else {
|
||||||
|
accordions.forEach((accordion) => closeAccordion(accordion));
|
||||||
|
openAccordion(accordion);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}(Drupal, once));
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
.ib-accordion {
|
||||||
|
|
||||||
|
&__item {
|
||||||
|
margin: 10px 0;
|
||||||
|
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
|
||||||
|
box-shadow: 0 1px 1px rgba(0, 0, 0, .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__headline {
|
||||||
|
background-color: #EFEFEF;
|
||||||
|
padding: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-size: 18px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
color: #3272b3;
|
||||||
|
--bs-heading-color: #3272b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
display: flex;
|
||||||
|
float: left;
|
||||||
|
content: "\002B";
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 700;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--site-primary);
|
||||||
|
border-radius: 100%;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
color: #fff;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
padding: 0 20px;
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
will-change: max-height;
|
||||||
|
transition: all 0.25s ease-out;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> :first-child {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__active {
|
||||||
|
.ib-accordion__headline::before {
|
||||||
|
content: "\2212";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
{% set classes = [
|
||||||
|
'ib-accordion__item',
|
||||||
|
'ib-accordion__' ~ type|clean_class,
|
||||||
|
] %}
|
||||||
|
|
||||||
|
{% set ariaLabel = 'accordion-' ~ random(0, 10000) %}
|
||||||
|
<div class="{{ classes|join(" ") }}">
|
||||||
|
<div class="ib-accordion__headline">
|
||||||
|
{% block headline %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
<div class="ib-accordion__body" aria-labelledby="{{ ariaLabel }}" aria-expanded="false">
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
name: Banner
|
||||||
|
props:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
bannerText:
|
||||||
|
type: string
|
||||||
|
title: Banner Text
|
||||||
|
description: This is the text for the banner.
|
||||||
|
bannerImage:
|
||||||
|
type: string
|
||||||
|
title: Banner Image
|
||||||
|
description: This is the image for the banner.
|
||||||
|
bannerType:
|
||||||
|
type: string
|
||||||
|
title: Banner Type
|
||||||
|
description: The type of banner.
|
||||||
|
enum: ["full", "thin"]
|
||||||
|
addClasses:
|
||||||
|
type: string
|
||||||
|
title: Additonal Classes
|
||||||
|
description: String of additional classses to add to the banner.
|
||||||
@ -0,0 +1 @@
|
|||||||
|
.banner__thin{height:192px}.banner__thin .banner__text{position:relative;top:50%;transform:translateY(-50%)}.banner-image{background-size:cover;background-position:center;background-repeat:no-repeat}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
.banner {
|
||||||
|
|
||||||
|
&__thin {
|
||||||
|
height: 192px;
|
||||||
|
|
||||||
|
.banner__text {
|
||||||
|
position: relative;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
{%
|
||||||
|
set classes = [
|
||||||
|
'banner',
|
||||||
|
'banner__' ~ bannerType|clean_class,
|
||||||
|
]
|
||||||
|
%}
|
||||||
|
|
||||||
|
{% if addClasses %}
|
||||||
|
{% set classes = classes|merge([addClasses]) %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="{{ classes|join(" ") }}" style="background-image: url({{ bannerImage }})">
|
||||||
|
<div class="banner__text container">
|
||||||
|
{{ include('lawa:heading', { level: '1', headingText: bannerText, color: textColor }, with_context = false) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
name: Heading
|
||||||
|
props:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- level
|
||||||
|
properties:
|
||||||
|
level:
|
||||||
|
type: string
|
||||||
|
title: Heading Level
|
||||||
|
description: 'The level of the heading, i.e. h1'
|
||||||
|
enum: ['1', '2', '3', '4', '5', '6']
|
||||||
|
headingText:
|
||||||
|
type: string
|
||||||
|
title: Heading Text
|
||||||
|
description: 'The text within the heading.'
|
||||||
|
color:
|
||||||
|
type: string
|
||||||
|
title: Heading Text Color
|
||||||
|
description: The color of the text for the heading.
|
||||||
|
enum: ['primary', 'secondary', 'white', 'black', 'neutral', 'teal']
|
||||||
|
addClasses:
|
||||||
|
type: string
|
||||||
|
title: Classes
|
||||||
|
description: Additional classes for the heading.
|
||||||
|
subheading:
|
||||||
|
type: string
|
||||||
|
title: Subheading
|
||||||
|
description: Text that can appear under the heading.
|
||||||
@ -0,0 +1 @@
|
|||||||
|
.heading{margin:0}.heading__primary{color:#1d53a3}.heading__white{color:#fff}.heading__black{color:#000}.heading__neutral{color:#121212}.heading__teal{color:#01b9b6}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
@import 'src/sass/partials/variables';
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&__primary {
|
||||||
|
color: $primary_navy;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__white {
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__black {
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__neutral {
|
||||||
|
color: $neutrals;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__teal {
|
||||||
|
color: $teal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
{% set classes = ['heading'] %}
|
||||||
|
|
||||||
|
{% if color %}
|
||||||
|
{% set classes = classes|merge(['heading__' ~ color|clean_class]) %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if addClasses %}
|
||||||
|
{% set classes = classes|merge([addClasses])%}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% set heading = headingText.0['#context']['value']|default(headingText) %}
|
||||||
|
|
||||||
|
{% if ariaLabel %}
|
||||||
|
<h{{ level }} id="{{ ariaLabel }}" class="{{ classes|join(" ")}}">{{ heading }}</h{{ level}}>
|
||||||
|
{% else %}
|
||||||
|
<h{{ level }} class="{{ classes|join(" ")}}">
|
||||||
|
{{ heading }}
|
||||||
|
{% if subheading %}
|
||||||
|
<br><em>{{ subheading }}</em>
|
||||||
|
{% endif %}
|
||||||
|
</h{{ level}}>
|
||||||
|
{% endif %}
|
||||||
@ -1,2 +0,0 @@
|
|||||||
.idea
|
|
||||||
.idea/
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
## JSON Ingestion Examples
|
|
||||||
|
|
||||||
The data is pulled from https://jsonplaceholder.typicode.com/
|
|
||||||
|
|
||||||
### Approach 1: Migrate API
|
|
||||||
#### Requirements
|
|
||||||
- Migrate
|
|
||||||
- Migrate Plus
|
|
||||||
|
|
||||||
There are 2 migrations:
|
|
||||||
- images (5000 total)
|
|
||||||
- json_posts_to_posts (100 total)
|
|
||||||
|
|
||||||
To run them you can do:
|
|
||||||
```
|
|
||||||
drush mim images --limit=100
|
|
||||||
drush mim json_posts_to_posts
|
|
||||||
```
|
|
||||||
there are only 100 posts, so for our purposes we don't need more than 100 images.
|
|
||||||
|
|
||||||
Once both migrations have been run you can see the data at `/migration-posts` which is controller by a view.
|
|
||||||
This approach has the advantage of requiring migrate API which pulls in a lot of helpful things,
|
|
||||||
such as rollbacks, update old items, only importing new items...etc.
|
|
||||||
### Approach 2: Custom Drush Command
|
|
||||||
Will import the first 100 images only, followed by 100 posts.
|
|
||||||
Also available for viewing on `/migration-posts` after import.
|
|
||||||
```
|
|
||||||
drush jie-posts
|
|
||||||
```
|
|
||||||
### Approach 3: Views JSON Source
|
|
||||||
#### Requirements
|
|
||||||
- views_json_source
|
|
||||||
|
|
||||||
Make sure `views_json_source` module is included in your codebase.
|
|
||||||
Then visit `/views-json-source` after enabling this module which is a preconfigured view of titles and body fields from external JSON.
|
|
||||||
|
|
||||||
### Approach 4: Javascript Only
|
|
||||||
1. Attach a javascript library to a page
|
|
||||||
2. Fetch the API data and render it all client side
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "drupal/json_ingestion_examples",
|
|
||||||
"type": "drupal-module",
|
|
||||||
"license": "GPL-2.0-or-later",
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Dan Chadwick",
|
|
||||||
"email": "dan.chadwick@acquia.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"require": {
|
|
||||||
"drupal/core": "^9 || ^10",
|
|
||||||
"drupal/migrate_plus": "^6.0",
|
|
||||||
"drupal/views_json_source": "^1.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
langcode: en
|
|
||||||
status: true
|
|
||||||
dependencies:
|
|
||||||
config:
|
|
||||||
- field.field.node.post.body
|
|
||||||
- field.field.node.post.field_image
|
|
||||||
- image.style.thumbnail
|
|
||||||
- node.type.post
|
|
||||||
module:
|
|
||||||
- image
|
|
||||||
- path
|
|
||||||
- text
|
|
||||||
id: node.post.default
|
|
||||||
targetEntityType: node
|
|
||||||
bundle: post
|
|
||||||
mode: default
|
|
||||||
content:
|
|
||||||
body:
|
|
||||||
type: text_textarea_with_summary
|
|
||||||
weight: 121
|
|
||||||
region: content
|
|
||||||
settings:
|
|
||||||
rows: 9
|
|
||||||
summary_rows: 3
|
|
||||||
placeholder: ''
|
|
||||||
show_summary: false
|
|
||||||
third_party_settings: { }
|
|
||||||
created:
|
|
||||||
type: datetime_timestamp
|
|
||||||
weight: 10
|
|
||||||
region: content
|
|
||||||
settings: { }
|
|
||||||
third_party_settings: { }
|
|
||||||
field_image:
|
|
||||||
type: image_image
|
|
||||||
weight: 1
|
|
||||||
region: content
|
|
||||||
settings:
|
|
||||||
progress_indicator: throbber
|
|
||||||
preview_image_style: thumbnail
|
|
||||||
third_party_settings: { }
|
|
||||||
path:
|
|
||||||
type: path
|
|
||||||
weight: 30
|
|
||||||
region: content
|
|
||||||
settings: { }
|
|
||||||
third_party_settings: { }
|
|
||||||
promote:
|
|
||||||
type: boolean_checkbox
|
|
||||||
weight: 15
|
|
||||||
region: content
|
|
||||||
settings:
|
|
||||||
display_label: true
|
|
||||||
third_party_settings: { }
|
|
||||||
status:
|
|
||||||
type: boolean_checkbox
|
|
||||||
weight: 120
|
|
||||||
region: content
|
|
||||||
settings:
|
|
||||||
display_label: true
|
|
||||||
third_party_settings: { }
|
|
||||||
sticky:
|
|
||||||
type: boolean_checkbox
|
|
||||||
weight: 16
|
|
||||||
region: content
|
|
||||||
settings:
|
|
||||||
display_label: true
|
|
||||||
third_party_settings: { }
|
|
||||||
title:
|
|
||||||
type: string_textfield
|
|
||||||
weight: -5
|
|
||||||
region: content
|
|
||||||
settings:
|
|
||||||
size: 60
|
|
||||||
placeholder: ''
|
|
||||||
third_party_settings: { }
|
|
||||||
uid:
|
|
||||||
type: entity_reference_autocomplete
|
|
||||||
weight: 5
|
|
||||||
region: content
|
|
||||||
settings:
|
|
||||||
match_operator: CONTAINS
|
|
||||||
match_limit: 10
|
|
||||||
size: 60
|
|
||||||
placeholder: ''
|
|
||||||
third_party_settings: { }
|
|
||||||
hidden: { }
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
langcode: en
|
|
||||||
status: true
|
|
||||||
dependencies:
|
|
||||||
config:
|
|
||||||
- field.field.node.post.body
|
|
||||||
- field.field.node.post.field_image
|
|
||||||
- image.style.wide
|
|
||||||
- node.type.post
|
|
||||||
module:
|
|
||||||
- image
|
|
||||||
- text
|
|
||||||
- user
|
|
||||||
id: node.post.default
|
|
||||||
targetEntityType: node
|
|
||||||
bundle: post
|
|
||||||
mode: default
|
|
||||||
content:
|
|
||||||
body:
|
|
||||||
type: text_default
|
|
||||||
label: hidden
|
|
||||||
settings: { }
|
|
||||||
third_party_settings: { }
|
|
||||||
weight: 101
|
|
||||||
region: content
|
|
||||||
field_image:
|
|
||||||
type: image
|
|
||||||
label: hidden
|
|
||||||
settings:
|
|
||||||
image_link: ''
|
|
||||||
image_style: wide
|
|
||||||
image_loading:
|
|
||||||
attribute: eager
|
|
||||||
third_party_settings: { }
|
|
||||||
weight: -1
|
|
||||||
region: content
|
|
||||||
links:
|
|
||||||
settings: { }
|
|
||||||
third_party_settings: { }
|
|
||||||
weight: 100
|
|
||||||
region: content
|
|
||||||
hidden: { }
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
langcode: en
|
|
||||||
status: true
|
|
||||||
dependencies:
|
|
||||||
config:
|
|
||||||
- core.entity_view_mode.node.teaser
|
|
||||||
- field.field.node.post.body
|
|
||||||
- field.field.node.post.field_image
|
|
||||||
- image.style.medium
|
|
||||||
- node.type.post
|
|
||||||
module:
|
|
||||||
- image
|
|
||||||
- text
|
|
||||||
- user
|
|
||||||
id: node.post.teaser
|
|
||||||
targetEntityType: node
|
|
||||||
bundle: post
|
|
||||||
mode: teaser
|
|
||||||
content:
|
|
||||||
body:
|
|
||||||
type: text_summary_or_trimmed
|
|
||||||
label: hidden
|
|
||||||
settings:
|
|
||||||
trim_length: 600
|
|
||||||
third_party_settings: { }
|
|
||||||
weight: 101
|
|
||||||
region: content
|
|
||||||
field_image:
|
|
||||||
type: image
|
|
||||||
label: hidden
|
|
||||||
settings:
|
|
||||||
image_link: content
|
|
||||||
image_style: medium
|
|
||||||
image_loading:
|
|
||||||
attribute: lazy
|
|
||||||
third_party_settings: { }
|
|
||||||
weight: -1
|
|
||||||
region: content
|
|
||||||
links:
|
|
||||||
settings: { }
|
|
||||||
third_party_settings: { }
|
|
||||||
weight: 100
|
|
||||||
region: content
|
|
||||||
hidden: { }
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
langcode: en
|
|
||||||
status: true
|
|
||||||
dependencies:
|
|
||||||
config:
|
|
||||||
- field.storage.node.body
|
|
||||||
- node.type.post
|
|
||||||
module:
|
|
||||||
- text
|
|
||||||
id: node.post.body
|
|
||||||
field_name: body
|
|
||||||
entity_type: node
|
|
||||||
bundle: post
|
|
||||||
label: Body
|
|
||||||
description: ''
|
|
||||||
required: false
|
|
||||||
translatable: true
|
|
||||||
default_value: { }
|
|
||||||
default_value_callback: ''
|
|
||||||
settings:
|
|
||||||
display_summary: true
|
|
||||||
required_summary: false
|
|
||||||
allowed_formats: { }
|
|
||||||
field_type: text_with_summary
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
langcode: en
|
|
||||||
status: true
|
|
||||||
dependencies:
|
|
||||||
config:
|
|
||||||
- field.storage.node.field_image
|
|
||||||
- node.type.post
|
|
||||||
module:
|
|
||||||
- image
|
|
||||||
id: node.post.field_image
|
|
||||||
field_name: field_image
|
|
||||||
entity_type: node
|
|
||||||
bundle: post
|
|
||||||
label: Image
|
|
||||||
description: ''
|
|
||||||
required: false
|
|
||||||
translatable: false
|
|
||||||
default_value: { }
|
|
||||||
default_value_callback: ''
|
|
||||||
settings:
|
|
||||||
handler: 'default:file'
|
|
||||||
handler_settings: { }
|
|
||||||
file_directory: '[date:custom:Y]-[date:custom:m]'
|
|
||||||
file_extensions: 'png gif jpg jpeg webp'
|
|
||||||
max_filesize: ''
|
|
||||||
max_resolution: ''
|
|
||||||
min_resolution: ''
|
|
||||||
alt_field: true
|
|
||||||
alt_field_required: true
|
|
||||||
title_field: false
|
|
||||||
title_field_required: false
|
|
||||||
default_image:
|
|
||||||
uuid: ''
|
|
||||||
alt: ''
|
|
||||||
title: ''
|
|
||||||
width: null
|
|
||||||
height: null
|
|
||||||
field_type: image
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
langcode: en
|
|
||||||
status: true
|
|
||||||
dependencies:
|
|
||||||
module:
|
|
||||||
- file
|
|
||||||
- image
|
|
||||||
- node
|
|
||||||
_core:
|
|
||||||
default_config_hash: EymokncRIZ7SgQT2IdOQhQJicX4nNc0K89ik-LxmOHE
|
|
||||||
id: node.field_image
|
|
||||||
field_name: field_image
|
|
||||||
entity_type: node
|
|
||||||
type: image
|
|
||||||
settings:
|
|
||||||
target_type: file
|
|
||||||
display_field: false
|
|
||||||
display_default: false
|
|
||||||
uri_scheme: public
|
|
||||||
default_image:
|
|
||||||
uuid: ''
|
|
||||||
alt: ''
|
|
||||||
title: ''
|
|
||||||
width: null
|
|
||||||
height: null
|
|
||||||
module: image
|
|
||||||
locked: false
|
|
||||||
cardinality: 1
|
|
||||||
translatable: true
|
|
||||||
indexes:
|
|
||||||
target_id:
|
|
||||||
- target_id
|
|
||||||
persist_with_no_fields: false
|
|
||||||
custom_storage: false
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
langcode: en
|
|
||||||
status: true
|
|
||||||
dependencies:
|
|
||||||
module:
|
|
||||||
- menu_ui
|
|
||||||
third_party_settings:
|
|
||||||
menu_ui:
|
|
||||||
available_menus:
|
|
||||||
- main
|
|
||||||
parent: 'main:'
|
|
||||||
name: Post
|
|
||||||
type: post
|
|
||||||
description: ''
|
|
||||||
help: ''
|
|
||||||
new_revision: true
|
|
||||||
preview_mode: 1
|
|
||||||
display_submitted: true
|
|
||||||
@ -1,191 +0,0 @@
|
|||||||
langcode: en
|
|
||||||
status: true
|
|
||||||
dependencies:
|
|
||||||
config:
|
|
||||||
- core.entity_view_mode.node.teaser
|
|
||||||
- node.type.post
|
|
||||||
module:
|
|
||||||
- node
|
|
||||||
- user
|
|
||||||
id: migration_based_posts
|
|
||||||
label: 'Migration Based Posts'
|
|
||||||
module: views
|
|
||||||
description: ''
|
|
||||||
tag: ''
|
|
||||||
base_table: node_field_data
|
|
||||||
base_field: nid
|
|
||||||
display:
|
|
||||||
default:
|
|
||||||
id: default
|
|
||||||
display_title: Default
|
|
||||||
display_plugin: default
|
|
||||||
position: 0
|
|
||||||
display_options:
|
|
||||||
title: 'Migration Based Posts'
|
|
||||||
fields:
|
|
||||||
title:
|
|
||||||
id: title
|
|
||||||
table: node_field_data
|
|
||||||
field: title
|
|
||||||
relationship: none
|
|
||||||
group_type: group
|
|
||||||
admin_label: ''
|
|
||||||
entity_type: node
|
|
||||||
entity_field: title
|
|
||||||
plugin_id: field
|
|
||||||
label: ''
|
|
||||||
exclude: false
|
|
||||||
alter:
|
|
||||||
alter_text: false
|
|
||||||
make_link: false
|
|
||||||
absolute: false
|
|
||||||
word_boundary: false
|
|
||||||
ellipsis: false
|
|
||||||
strip_tags: false
|
|
||||||
trim: false
|
|
||||||
html: false
|
|
||||||
element_type: ''
|
|
||||||
element_class: ''
|
|
||||||
element_label_type: ''
|
|
||||||
element_label_class: ''
|
|
||||||
element_label_colon: true
|
|
||||||
element_wrapper_type: ''
|
|
||||||
element_wrapper_class: ''
|
|
||||||
element_default_classes: true
|
|
||||||
empty: ''
|
|
||||||
hide_empty: false
|
|
||||||
empty_zero: false
|
|
||||||
hide_alter_empty: true
|
|
||||||
click_sort_column: value
|
|
||||||
type: string
|
|
||||||
settings:
|
|
||||||
link_to_entity: true
|
|
||||||
group_column: value
|
|
||||||
group_columns: { }
|
|
||||||
group_rows: true
|
|
||||||
delta_limit: 0
|
|
||||||
delta_offset: 0
|
|
||||||
delta_reversed: false
|
|
||||||
delta_first_last: false
|
|
||||||
multi_type: separator
|
|
||||||
separator: ', '
|
|
||||||
field_api_classes: false
|
|
||||||
pager:
|
|
||||||
type: mini
|
|
||||||
options:
|
|
||||||
offset: 0
|
|
||||||
items_per_page: 10
|
|
||||||
total_pages: null
|
|
||||||
id: 0
|
|
||||||
tags:
|
|
||||||
next: ››
|
|
||||||
previous: ‹‹
|
|
||||||
expose:
|
|
||||||
items_per_page: false
|
|
||||||
items_per_page_label: 'Items per page'
|
|
||||||
items_per_page_options: '5, 10, 25, 50'
|
|
||||||
items_per_page_options_all: false
|
|
||||||
items_per_page_options_all_label: '- All -'
|
|
||||||
offset: false
|
|
||||||
offset_label: Offset
|
|
||||||
exposed_form:
|
|
||||||
type: basic
|
|
||||||
options:
|
|
||||||
submit_button: Apply
|
|
||||||
reset_button: false
|
|
||||||
reset_button_label: Reset
|
|
||||||
exposed_sorts_label: 'Sort by'
|
|
||||||
expose_sort_order: true
|
|
||||||
sort_asc_label: Asc
|
|
||||||
sort_desc_label: Desc
|
|
||||||
access:
|
|
||||||
type: perm
|
|
||||||
options:
|
|
||||||
perm: 'access content'
|
|
||||||
cache:
|
|
||||||
type: tag
|
|
||||||
options: { }
|
|
||||||
empty: { }
|
|
||||||
sorts:
|
|
||||||
created:
|
|
||||||
id: created
|
|
||||||
table: node_field_data
|
|
||||||
field: created
|
|
||||||
relationship: none
|
|
||||||
group_type: group
|
|
||||||
admin_label: ''
|
|
||||||
entity_type: node
|
|
||||||
entity_field: created
|
|
||||||
plugin_id: date
|
|
||||||
order: DESC
|
|
||||||
expose:
|
|
||||||
label: ''
|
|
||||||
field_identifier: ''
|
|
||||||
exposed: false
|
|
||||||
granularity: second
|
|
||||||
arguments: { }
|
|
||||||
filters:
|
|
||||||
status:
|
|
||||||
id: status
|
|
||||||
table: node_field_data
|
|
||||||
field: status
|
|
||||||
entity_type: node
|
|
||||||
entity_field: status
|
|
||||||
plugin_id: boolean
|
|
||||||
value: '1'
|
|
||||||
group: 1
|
|
||||||
expose:
|
|
||||||
operator: ''
|
|
||||||
type:
|
|
||||||
id: type
|
|
||||||
table: node_field_data
|
|
||||||
field: type
|
|
||||||
entity_type: node
|
|
||||||
entity_field: type
|
|
||||||
plugin_id: bundle
|
|
||||||
value:
|
|
||||||
post: post
|
|
||||||
style:
|
|
||||||
type: default
|
|
||||||
row:
|
|
||||||
type: 'entity:node'
|
|
||||||
options:
|
|
||||||
view_mode: teaser
|
|
||||||
query:
|
|
||||||
type: views_query
|
|
||||||
options:
|
|
||||||
query_comment: ''
|
|
||||||
disable_sql_rewrite: false
|
|
||||||
distinct: false
|
|
||||||
replica: false
|
|
||||||
query_tags: { }
|
|
||||||
relationships: { }
|
|
||||||
header: { }
|
|
||||||
footer: { }
|
|
||||||
display_extenders: { }
|
|
||||||
cache_metadata:
|
|
||||||
max-age: -1
|
|
||||||
contexts:
|
|
||||||
- 'languages:language_content'
|
|
||||||
- 'languages:language_interface'
|
|
||||||
- url.query_args
|
|
||||||
- 'user.node_grants:view'
|
|
||||||
- user.permissions
|
|
||||||
tags: { }
|
|
||||||
page_1:
|
|
||||||
id: page_1
|
|
||||||
display_title: Page
|
|
||||||
display_plugin: page
|
|
||||||
position: 1
|
|
||||||
display_options:
|
|
||||||
display_extenders: { }
|
|
||||||
path: migration-posts
|
|
||||||
cache_metadata:
|
|
||||||
max-age: -1
|
|
||||||
contexts:
|
|
||||||
- 'languages:language_content'
|
|
||||||
- 'languages:language_interface'
|
|
||||||
- url.query_args
|
|
||||||
- 'user.node_grants:view'
|
|
||||||
- user.permissions
|
|
||||||
tags: { }
|
|
||||||
@ -1,199 +0,0 @@
|
|||||||
langcode: en
|
|
||||||
status: true
|
|
||||||
dependencies:
|
|
||||||
module:
|
|
||||||
- views_json_source
|
|
||||||
id: views_json_source_demo
|
|
||||||
label: 'Views JSON Source'
|
|
||||||
module: views
|
|
||||||
description: ''
|
|
||||||
tag: ''
|
|
||||||
base_table: json
|
|
||||||
base_field: ''
|
|
||||||
display:
|
|
||||||
default:
|
|
||||||
id: default
|
|
||||||
display_title: Default
|
|
||||||
display_plugin: default
|
|
||||||
position: 0
|
|
||||||
display_options:
|
|
||||||
title: 'Views JSON Source'
|
|
||||||
fields:
|
|
||||||
value:
|
|
||||||
id: value
|
|
||||||
table: json
|
|
||||||
field: value
|
|
||||||
relationship: none
|
|
||||||
group_type: group
|
|
||||||
admin_label: ''
|
|
||||||
entity_type: null
|
|
||||||
entity_field: null
|
|
||||||
plugin_id: views_json_source_field
|
|
||||||
label: ''
|
|
||||||
exclude: false
|
|
||||||
alter:
|
|
||||||
alter_text: false
|
|
||||||
text: ''
|
|
||||||
make_link: false
|
|
||||||
path: ''
|
|
||||||
absolute: false
|
|
||||||
external: false
|
|
||||||
replace_spaces: false
|
|
||||||
path_case: none
|
|
||||||
trim_whitespace: false
|
|
||||||
alt: ''
|
|
||||||
rel: ''
|
|
||||||
link_class: ''
|
|
||||||
prefix: ''
|
|
||||||
suffix: ''
|
|
||||||
target: ''
|
|
||||||
nl2br: false
|
|
||||||
max_length: 0
|
|
||||||
word_boundary: true
|
|
||||||
ellipsis: true
|
|
||||||
more_link: false
|
|
||||||
more_link_text: ''
|
|
||||||
more_link_path: ''
|
|
||||||
strip_tags: false
|
|
||||||
trim: false
|
|
||||||
preserve_tags: ''
|
|
||||||
html: false
|
|
||||||
element_type: h2
|
|
||||||
element_class: ''
|
|
||||||
element_label_type: ''
|
|
||||||
element_label_class: ''
|
|
||||||
element_label_colon: false
|
|
||||||
element_wrapper_type: ''
|
|
||||||
element_wrapper_class: ''
|
|
||||||
element_default_classes: true
|
|
||||||
empty: ''
|
|
||||||
hide_empty: false
|
|
||||||
empty_zero: false
|
|
||||||
hide_alter_empty: true
|
|
||||||
key: title
|
|
||||||
trusted_html: 0
|
|
||||||
value_1:
|
|
||||||
id: value_1
|
|
||||||
table: json
|
|
||||||
field: value
|
|
||||||
relationship: none
|
|
||||||
group_type: group
|
|
||||||
admin_label: ''
|
|
||||||
plugin_id: views_json_source_field
|
|
||||||
label: ''
|
|
||||||
exclude: false
|
|
||||||
alter:
|
|
||||||
alter_text: false
|
|
||||||
text: ''
|
|
||||||
make_link: false
|
|
||||||
path: ''
|
|
||||||
absolute: false
|
|
||||||
external: false
|
|
||||||
replace_spaces: false
|
|
||||||
path_case: none
|
|
||||||
trim_whitespace: false
|
|
||||||
alt: ''
|
|
||||||
rel: ''
|
|
||||||
link_class: ''
|
|
||||||
prefix: ''
|
|
||||||
suffix: ''
|
|
||||||
target: ''
|
|
||||||
nl2br: false
|
|
||||||
max_length: 0
|
|
||||||
word_boundary: true
|
|
||||||
ellipsis: true
|
|
||||||
more_link: false
|
|
||||||
more_link_text: ''
|
|
||||||
more_link_path: ''
|
|
||||||
strip_tags: false
|
|
||||||
trim: false
|
|
||||||
preserve_tags: ''
|
|
||||||
html: false
|
|
||||||
element_type: ''
|
|
||||||
element_class: ''
|
|
||||||
element_label_type: ''
|
|
||||||
element_label_class: ''
|
|
||||||
element_label_colon: false
|
|
||||||
element_wrapper_type: ''
|
|
||||||
element_wrapper_class: ''
|
|
||||||
element_default_classes: true
|
|
||||||
empty: ''
|
|
||||||
hide_empty: false
|
|
||||||
empty_zero: false
|
|
||||||
hide_alter_empty: true
|
|
||||||
key: body
|
|
||||||
trusted_html: 0
|
|
||||||
pager:
|
|
||||||
type: mini
|
|
||||||
options:
|
|
||||||
offset: 0
|
|
||||||
items_per_page: 10
|
|
||||||
total_pages: null
|
|
||||||
id: 0
|
|
||||||
tags:
|
|
||||||
next: ››
|
|
||||||
previous: ‹‹
|
|
||||||
expose:
|
|
||||||
items_per_page: false
|
|
||||||
items_per_page_label: 'Items per page'
|
|
||||||
items_per_page_options: '5, 10, 25, 50'
|
|
||||||
items_per_page_options_all: false
|
|
||||||
items_per_page_options_all_label: '- All -'
|
|
||||||
offset: false
|
|
||||||
offset_label: Offset
|
|
||||||
exposed_form:
|
|
||||||
type: basic
|
|
||||||
options:
|
|
||||||
submit_button: Apply
|
|
||||||
reset_button: false
|
|
||||||
reset_button_label: Reset
|
|
||||||
exposed_sorts_label: 'Sort by'
|
|
||||||
expose_sort_order: true
|
|
||||||
sort_asc_label: Asc
|
|
||||||
sort_desc_label: Desc
|
|
||||||
access:
|
|
||||||
type: none
|
|
||||||
options: { }
|
|
||||||
cache:
|
|
||||||
type: tag
|
|
||||||
options: { }
|
|
||||||
empty: { }
|
|
||||||
sorts: { }
|
|
||||||
arguments: { }
|
|
||||||
filters: { }
|
|
||||||
style:
|
|
||||||
type: default
|
|
||||||
row:
|
|
||||||
type: fields
|
|
||||||
query:
|
|
||||||
type: views_query
|
|
||||||
options:
|
|
||||||
json_file: 'https://jsonplaceholder.typicode.com/posts'
|
|
||||||
row_apath: /
|
|
||||||
headers: ''
|
|
||||||
single_payload: 0
|
|
||||||
show_errors: 1
|
|
||||||
relationships: { }
|
|
||||||
header: { }
|
|
||||||
footer: { }
|
|
||||||
display_extenders: { }
|
|
||||||
cache_metadata:
|
|
||||||
max-age: -1
|
|
||||||
contexts:
|
|
||||||
- 'languages:language_interface'
|
|
||||||
- url.query_args
|
|
||||||
tags: { }
|
|
||||||
page_1:
|
|
||||||
id: page_1
|
|
||||||
display_title: Page
|
|
||||||
display_plugin: page
|
|
||||||
position: 1
|
|
||||||
display_options:
|
|
||||||
display_extenders: { }
|
|
||||||
path: views-json-source
|
|
||||||
cache_metadata:
|
|
||||||
max-age: -1
|
|
||||||
contexts:
|
|
||||||
- 'languages:language_interface'
|
|
||||||
- url.query_args
|
|
||||||
tags: { }
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
services:
|
|
||||||
json_ingestion_examples.commands:
|
|
||||||
class: Drupal\json_ingestion_examples\Drush\Commands\JsonIngestionCommands
|
|
||||||
tags:
|
|
||||||
- { name: drush.command }
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
name: JSON Ingestion Examples
|
|
||||||
type: module
|
|
||||||
package: custom
|
|
||||||
description: 'Ways to import JSON data to Drupal'
|
|
||||||
core_version_requirement: ^9 || ^10
|
|
||||||
dependencies:
|
|
||||||
- drupal:migrate
|
|
||||||
- drupal:migrate_plus
|
|
||||||
- drupal:views_json_source
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
function json_ingestion_examples_install() {
|
|
||||||
$mod_installer = \Drupal::service('module_installer');
|
|
||||||
$mod_installer->install(['migrate', 'migrate_plus', 'views_json_source']);
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
id: images
|
|
||||||
label: 'Import remote images.'
|
|
||||||
source:
|
|
||||||
plugin: url
|
|
||||||
data_fetcher_plugin: http
|
|
||||||
data_parser_plugin: json
|
|
||||||
urls: 'https://jsonplaceholder.typicode.com/photos'
|
|
||||||
item_selector: /
|
|
||||||
constants:
|
|
||||||
file_dest_dir: 'public://migrated-images/'
|
|
||||||
jpeg: '.jpeg'
|
|
||||||
ids:
|
|
||||||
image_id:
|
|
||||||
type: string
|
|
||||||
fields:
|
|
||||||
-
|
|
||||||
name: image_id
|
|
||||||
label: 'Image ID'
|
|
||||||
selector: /id
|
|
||||||
-
|
|
||||||
name: image_title
|
|
||||||
label: 'Image URL'
|
|
||||||
selector: /title
|
|
||||||
-
|
|
||||||
name: image_url
|
|
||||||
label: 'Image URL'
|
|
||||||
selector: /url
|
|
||||||
process:
|
|
||||||
_prep_filename:
|
|
||||||
-
|
|
||||||
plugin: callback
|
|
||||||
callable: basename
|
|
||||||
source: image_url
|
|
||||||
filename:
|
|
||||||
plugin: concat
|
|
||||||
source:
|
|
||||||
- '@_prep_filename'
|
|
||||||
- constants/jpeg
|
|
||||||
_prep_file_destination:
|
|
||||||
plugin: concat
|
|
||||||
source:
|
|
||||||
- constants/file_dest_dir
|
|
||||||
- '@filename'
|
|
||||||
uri:
|
|
||||||
plugin: file_copy
|
|
||||||
source:
|
|
||||||
- image_url
|
|
||||||
- '@_prep_file_destination'
|
|
||||||
status:
|
|
||||||
plugin: default_value
|
|
||||||
default_value: 1
|
|
||||||
alt: image_title
|
|
||||||
destination:
|
|
||||||
plugin: 'entity:file'
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
id: json_posts_to_posts
|
|
||||||
label: 'JSON Migrate - Posts to Posts'
|
|
||||||
migration_group: migrate_posts
|
|
||||||
source:
|
|
||||||
plugin: url
|
|
||||||
data_fetcher_plugin: http
|
|
||||||
data_parser_plugin: json
|
|
||||||
urls: 'https://jsonplaceholder.typicode.com/posts'
|
|
||||||
item_selector: /
|
|
||||||
ids:
|
|
||||||
post_id:
|
|
||||||
type: string
|
|
||||||
fields:
|
|
||||||
-
|
|
||||||
name: post_id
|
|
||||||
label: 'Post ID'
|
|
||||||
selector: /id
|
|
||||||
-
|
|
||||||
name: post_title
|
|
||||||
label: 'Post Title'
|
|
||||||
selector: /title
|
|
||||||
-
|
|
||||||
name: post_body
|
|
||||||
label: 'Post Body'
|
|
||||||
selector: /body
|
|
||||||
process:
|
|
||||||
title: post_title
|
|
||||||
body/value: post_body
|
|
||||||
field_image:
|
|
||||||
plugin: migration_lookup
|
|
||||||
migration: images
|
|
||||||
source: post_id # this gets an image with the same ID as the post.
|
|
||||||
|
|
||||||
destination:
|
|
||||||
plugin: 'entity:node'
|
|
||||||
default_bundle: post
|
|
||||||
migration_dependencies: { }
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Drupal\json_ingestion_examples\Drush\Commands;
|
|
||||||
|
|
||||||
use Drupal\Core\File\FileSystemInterface;
|
|
||||||
use Drupal\file\Entity\File;
|
|
||||||
use Drupal\node\Entity\Node;
|
|
||||||
use Drush\Commands\DrushCommands;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Drush command file.
|
|
||||||
*/
|
|
||||||
class JsonIngestionCommands extends DrushCommands {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A custom Drush command to displays the given text.
|
|
||||||
*
|
|
||||||
* @command json-ingestion-examples:update-posts
|
|
||||||
* The amount to iterate over
|
|
||||||
* @aliases jie-posts
|
|
||||||
*/
|
|
||||||
public function updatePosts() {
|
|
||||||
$this->importImages();
|
|
||||||
$this->importPosts();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports all images.
|
|
||||||
*/
|
|
||||||
public function importImages() {
|
|
||||||
// Go fetch JSON for images.
|
|
||||||
$http_client = \Drupal::httpClient();
|
|
||||||
$images_json_url = 'https://jsonplaceholder.typicode.com/photos';
|
|
||||||
$images_json = $http_client->request('GET', $images_json_url)->getBody()->getContents();
|
|
||||||
// Reduce to 100 images as only have 100 posts.
|
|
||||||
$images_array = array_slice(json_decode($images_json), 0, 100);
|
|
||||||
$directory = 'public://drush-generated';
|
|
||||||
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
|
|
||||||
$file_system = \Drupal::service('file_system');
|
|
||||||
// Make sure directory exists.
|
|
||||||
$file_system->prepareDirectory($directory, FileSystemInterface:: CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
|
|
||||||
foreach ($images_array as $img_ob) {
|
|
||||||
// Download file, always replace.
|
|
||||||
$file = file_get_contents($img_ob->url);
|
|
||||||
$filename = basename($img_ob->url) . '.jpeg';
|
|
||||||
$filepath = $directory . '/' . $filename;
|
|
||||||
$file_system->saveData($file, $filepath, FileSystemInterface::EXISTS_REPLACE);
|
|
||||||
// Create file entity.
|
|
||||||
$file = File::create([
|
|
||||||
'filename' => $filename,
|
|
||||||
'uri' => $filepath,
|
|
||||||
'status' => 1,
|
|
||||||
'uid' => 1,
|
|
||||||
]);
|
|
||||||
if ($file->save()) {
|
|
||||||
$this->writeln("File $filename created");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports all posts.
|
|
||||||
*/
|
|
||||||
public function importPosts() {
|
|
||||||
// Go fetch JSON for posts.
|
|
||||||
$http_client = \Drupal::httpClient();
|
|
||||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
|
||||||
$posts_json_url = 'https://jsonplaceholder.typicode.com/posts';
|
|
||||||
$posts_json = $http_client->request('GET', $posts_json_url)->getBody()->getContents();
|
|
||||||
$posts_array = json_decode($posts_json);
|
|
||||||
foreach ($posts_array as $post) {
|
|
||||||
$node_check = $node_storage->loadByProperties([
|
|
||||||
'type' => 'post',
|
|
||||||
'title' => $post->title
|
|
||||||
]);
|
|
||||||
// If title already exists, skip.
|
|
||||||
if ($node_check) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$new_post = Node::create(['type' => 'post']);
|
|
||||||
$new_post->set('title', $post->title);
|
|
||||||
$new_post->set('body', $post->body);
|
|
||||||
$new_post->set('field_image', $post->id);
|
|
||||||
$new_post->enforceIsNew();
|
|
||||||
if ($new_post->save()) {
|
|
||||||
$this->writeln("New post created: $post->title");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
name: Video Compressor
|
||||||
|
description: 'Compresses videos to smaller size prior to upload.'
|
||||||
|
core_version_requirement: ^10 || ^11
|
||||||
|
package: Media
|
||||||
|
type: module
|
||||||
10
web/modules/custom/video_compressor/video_compressor.module
Normal file
10
web/modules/custom/video_compressor/video_compressor.module
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Drupal\Core\Entity\EntityInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_entity_presave().
|
||||||
|
*/
|
||||||
|
function video_compressor_entity_presave(EntityInterface $entity) {
|
||||||
|
/* dump($entity); */
|
||||||
|
}
|
||||||
1408
web/themes/custom/dchadwick/package-lock.json
generated
1408
web/themes/custom/dchadwick/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,9 +11,12 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^10.4.20",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"gulp-concat": "^2.6.1",
|
"gulp-concat": "^2.6.1",
|
||||||
"gulp-sass": "^5.1.0"
|
"gulp-sass": "^5.1.0",
|
||||||
|
"postcss": "^8.4.47",
|
||||||
|
"tailwindcss": "^3.4.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sass": "^1.71.1"
|
"sass": "^1.71.1"
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
// Colors.
|
// Colors.
|
||||||
:root {
|
:root {
|
||||||
--site-primary: #009FB7;
|
--site-primary: #009FB7;
|
||||||
|
|||||||
12
web/themes/custom/dchadwick/tailwind.config.js
Normal file
12
web/themes/custom/dchadwick/tailwind.config.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["./src/**/**.{scss}"],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user