first commit

This commit is contained in:
2024-07-15 12:33:27 +02:00
commit ce50ae282b
22084 changed files with 2623791 additions and 0 deletions

3
core/scripts/cron-curl.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
curl --silent --compressed http://example.com/cron/YOURKEY

3
core/scripts/cron-lynx.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
/usr/bin/lynx -source http://example.com/cron/YOURKEY > /dev/null 2>&1

View File

@@ -0,0 +1,15 @@
const fs = require('fs');
const log = require('./log');
const compile = require('./compile');
module.exports = (filePath) => {
log(`'${filePath}' is being processed.`);
// Transform the file.
compile(filePath, function write(code) {
const fileName = filePath.slice(0, -9);
// Write the result to the filesystem.
fs.writeFile(`${fileName}.css`, code, () => {
log(`'${filePath}' is finished.`);
});
});
};

22
core/scripts/css/check.js Executable file
View File

@@ -0,0 +1,22 @@
const fs = require('fs');
const log = require('./log');
const compile = require('./compile');
module.exports = (filePath) => {
log(`'${filePath}' is being checked.`);
// Transform the file.
compile(filePath, function check(code) {
const fileName = filePath.slice(0, -9);
fs.readFile(`${fileName}.css`, function read(err, data) {
if (err) {
log(err);
process.exitCode = 1;
return;
}
if (code !== data.toString()) {
log(`'${filePath}' is not updated.`);
process.exitCode = 1;
}
});
});
};

81
core/scripts/css/compile.js Executable file
View File

@@ -0,0 +1,81 @@
const log = require('./log');
const fs = require('fs');
const postcss = require('postcss');
const postcssImport = require('postcss-import');
const postcssHeader = require('postcss-header');
const postcssUrl = require('postcss-url');
const postcssPresetEnv = require('postcss-preset-env');
// cspell:ignore pxtorem
const postcssPixelsToRem = require('postcss-pxtorem');
const prettier = require('prettier');
const removeUnwantedComments = require('./remove-unwanted-comments');
module.exports = (filePath, callback) => {
// Transform the file.
fs.readFile(filePath, (err, css) => {
postcss([
postcssImport({
plugins: [
removeUnwantedComments,
],
}),
postcssPresetEnv({
stage: 1,
preserve: false,
autoprefixer: {
cascade: false,
grid: 'no-autoplace',
},
features: {
'blank-pseudo-class': false,
'focus-visible-pseudo-class': false,
'focus-within-pseudo-class': false,
'has-pseudo-class': false,
'image-set-function': false,
'prefers-color-scheme-query': false,
}
}),
postcssPixelsToRem({
propList: [
'*',
'!background-position',
'!border',
'!border-width',
'!box-shadow',
'!border-top*',
'!border-right*',
'!border-bottom*',
'!border-left*',
'!border-start*',
'!border-end*',
'!outline*',
],
mediaQuery: true,
minPixelValue: 3,
// Prevent converting PX to REM for icon styles. These files have been
// added to use the `postcssUrl` plugin, but aren't compatible with
// `postcssPixelsToRem`.
exclude: (filePath) => filePath.match(/core\/modules.*\.icons\..*\.pcss\.css$/)
}),
postcssHeader({
header: `/*\n * DO NOT EDIT THIS FILE.\n * See the following change record for more information,\n * https://www.drupal.org/node/3084859\n * @preserve\n */\n`,
}),
postcssUrl({
filter: '**/*.svg',
url: 'inline',
optimizeSvgEncode: true,
})
])
.process(css, { from: filePath })
.then(async result => {
const config = await prettier.resolveConfig(filePath);
return await prettier.format(result.css, config);
})
.then(callback)
.catch(error => {
log(error);
process.exitCode = 1;
});
});
};

4
core/scripts/css/log.js Executable file
View File

@@ -0,0 +1,4 @@
module.exports = (message) => {
// Logging human-readable timestamp.
console.log(`[${new Date().toTimeString().slice(0, 8)}] ${message}`);
};

View File

@@ -0,0 +1,46 @@
/**
* @file
*
* Provides the build:css command to compile *.pcss.css files to CSS.
*
* Run build:css with --file to only parse a specific file. Using the --check
* flag build:css can be run to check if files are compiled correctly.
* @example <caption>Only process misc/drupal.pcss.css and misc/drupal.init.pcss.css</caption>
* yarn run build:css --file misc/drupal.pcss.css --file misc/drupal.init.pcss.css
* @example <caption>Check if all files have been compiled correctly</caption>
* yarn run build:css --check
*
* @internal This file is part of the core CSS build process and is only
* designed to be used in that context.
*/
'use strict';
const { globSync } = require('glob');
const argv = require('minimist')(process.argv.slice(2));
const changeOrAdded = require('./changeOrAdded');
const check = require('./check');
const log = require('./log');
// Match only on .pcss.css files.
const fileMatch = './**/*.pcss.css';
// Ignore everything in node_modules
const globOptions = {
ignore: './node_modules/**'
};
const processFiles = (filePaths) => {
// Process all the found files.
let callback = changeOrAdded;
if (argv.check) {
callback = check;
}
filePaths.forEach(callback);
};
if (argv.file) {
processFiles([].concat(argv.file));
}
else {
processFiles(globSync(fileMatch, globOptions).sort());
}
process.exitCode = 0;

View File

@@ -0,0 +1,43 @@
/**
* @file
*
* Watch changes to *.pcss.css files and compile them to CSS during development.
*
* @internal This file is part of the core CSS build process and is only
* designed to be used in that context.
*/
'use strict';
const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');
const changeOrAdded = require('./changeOrAdded');
const log = require('./log');
// Match only on .pcss.css files.
const fileMatch = './**/*.pcss.css';
// Ignore everything in node_modules
const watcher = chokidar.watch(fileMatch, {
ignoreInitial: true,
ignored: './node_modules/**'
});
const unlinkHandler = (err) => {
if (err) {
log(err);
}
};
// Watch for filesystem changes.
watcher
.on('add', changeOrAdded)
.on('change', changeOrAdded)
.on('unlink', (filePath) => {
const fileName = filePath.slice(0, -9);
fs.stat(`${fileName}.css`, () => {
fs.unlink(`${fileName}.css`, unlinkHandler);
});
})
.on('ready', () => log(`Watching '${fileMatch}' for changes.`));

View File

@@ -0,0 +1,14 @@
// On import, remove the comments, so they don't appear as useless comments at the top of the autogenerated css files.
module.exports = opts => {
return {
postcssPlugin: 'remove-unwanted-comments',
Once(css) {
css.walk(node => {
if (node.type === 'comment') {
node.remove();
}
})
}
}
}
module.exports.postcss = true

26
core/scripts/db-tools.php Executable file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env php
<?php
/**
* @file
* A command line application to import a database generation script.
*/
use Drupal\Core\Command\DbToolsApplication;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\Request;
if (PHP_SAPI !== 'cli') {
return;
}
// Bootstrap.
$autoloader = require __DIR__ . '/../../autoload.php';
$request = Request::createFromGlobals();
Settings::initialize(dirname(__DIR__, 2), DrupalKernel::findSitePath($request), $autoloader);
DrupalKernel::createFromRequest($request, $autoloader, 'prod')->boot();
// Run the database dump command.
$application = new DbToolsApplication();
$application->run();

View File

@@ -0,0 +1,518 @@
#!/bin/bash
#
# This script performs code quality checks.
#
# @internal
# This script is not covered by Drupal core's backwards compatibility promise.
# It exists only for core development purposes.
#
# The script makes the following checks:
# - Spell checking.
# - File modes.
# - No changes to core/node_modules directory.
# - PHPCS checks PHP and YAML files.
# - PHPStan checks PHP files.
# - ESLint checks JavaScript and YAML files.
# - Stylelint checks CSS files.
# - Checks .pcss.css and .css files are equivalent.
# cSpell:disable
# Searches an array.
contains_element() {
local e
for e in ${@:2}; do [[ "$e" == "$1" ]] && return 0; done
return 1
}
CACHED=0
DRUPALCI=0
BRANCH=""
while test $# -gt 0; do
case "$1" in
-h|--help)
echo "Drupal code quality checks"
echo " "
echo "options:"
echo "-h, --help show brief help"
echo "--branch BRANCH creates list of files to check by comparing against a branch"
echo "--cached checks staged files"
echo "--drupalci a special mode for DrupalCI"
echo " "
echo "Example usage: sh ./core/scripts/dev/commit-code-check.sh --branch 9.2.x"
exit 0
;;
--branch)
BRANCH="$2"
if [[ "$BRANCH" == "" ]]; then
printf "The --branch option requires a value. For example: --branch 9.2.x\n"
exit;
fi
shift 2
;;
--cached)
CACHED=1
shift
;;
--drupalci)
DRUPALCI=1
shift
;;
*)
break
;;
esac
done
# Set up variables to make colored output simple. Color output is disabled on
# DrupalCI because it is breaks reporting.
# @todo https://www.drupal.org/project/drupalci_testbot/issues/3181869
if [[ "$DRUPALCI" == "1" ]]; then
red=""
green=""
reset=""
DRUPAL_VERSION=$(php -r "include 'vendor/autoload.php'; print preg_replace('#\.[0-9]+-dev#', '.x', \Drupal::VERSION);")
GIT="sudo -u www-data git"
else
red=$(tput setaf 1 && tput bold)
green=$(tput setaf 2)
reset=$(tput sgr0)
GIT="git"
fi
# Gets list of files to check.
if [[ "$BRANCH" != "" ]]; then
FILES=$($GIT diff --name-only $BRANCH HEAD);
elif [[ "$CACHED" == "0" ]]; then
# For DrupalCI patch testing or when running without --cached or --branch,
# list of all changes in the working directory.
FILES=$($GIT ls-files --other --modified --exclude-standard --exclude=vendor)
else
# Check staged files only.
if $GIT rev-parse --verify HEAD >/dev/null 2>&1
then
AGAINST=HEAD
else
# Initial commit: diff against an empty tree object
AGAINST=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
FILES=$($GIT diff --cached --name-only $AGAINST);
fi
if [[ "$FILES" == "" ]] && [[ "$DRUPALCI" == "1" ]]; then
# If the FILES is empty we might be testing a merge request on DrupalCI. We
# need to diff against the Drupal branch or tag related to the Drupal version.
printf "Creating list of files to check by comparing branch to %s\n" "$DRUPAL_VERSION"
# On DrupalCI there's a merge commit so we can compare to HEAD~1.
FILES=$($GIT diff --name-only HEAD~1 HEAD);
fi
TOP_LEVEL=$($GIT rev-parse --show-toplevel)
# This variable will be set to one when the file core/phpcs.xml.dist is changed.
PHPCS_XML_DIST_FILE_CHANGED=0
# This variable will be set to one when the files core/.phpstan-baseline.php or
# core/phpstan.neon.dist are changed.
PHPSTAN_DIST_FILE_CHANGED=0
# This variable will be set to one when one of the eslint config file is
# changed:
# - core/.eslintrc.passing.json
# - core/.eslintrc.json
# - core/.eslintrc.jquery.json
ESLINT_CONFIG_PASSING_FILE_CHANGED=0
# This variable will be set to one when the stylelint config file is changed.
# changed:
# - core/.stylelintignore
# - core/.stylelintrc.json
STYLELINT_CONFIG_FILE_CHANGED=0
# This variable will be set to one when JavaScript packages files are changed.
# changed:
# - core/package.json
# - core/yarn.lock
JAVASCRIPT_PACKAGES_CHANGED=0
# This variable will be set when a Drupal-specific CKEditor 5 plugin has changed
# it is used to make sure the compiled JS is valid.
CKEDITOR5_PLUGINS_CHANGED=0
# This variable will be set to when the dictionary has changed.
CSPELL_DICTIONARY_FILE_CHANGED=0
# Build up a list of absolute file names.
ABS_FILES=
for FILE in $FILES; do
if [ -f "$TOP_LEVEL/$FILE" ]; then
ABS_FILES="$ABS_FILES $TOP_LEVEL/$FILE"
fi
if [[ $FILE == "core/phpcs.xml.dist" ]]; then
PHPCS_XML_DIST_FILE_CHANGED=1;
fi;
if [[ $FILE == "core/.phpstan-baseline.php" || $FILE == "core/phpstan.neon.dist" ]]; then
PHPSTAN_DIST_FILE_CHANGED=1;
fi;
if [[ $FILE == "core/.eslintrc.json" || $FILE == "core/.eslintrc.passing.json" || $FILE == "core/.eslintrc.jquery.json" ]]; then
ESLINT_CONFIG_PASSING_FILE_CHANGED=1;
fi;
if [[ $FILE == "core/.stylelintignore" || $FILE == "core/.stylelintrc.json" ]]; then
STYLELINT_CONFIG_FILE_CHANGED=1;
fi;
# If JavaScript packages change, then rerun all JavaScript style checks.
if [[ $FILE == "core/package.json" || $FILE == "core/yarn.lock" ]]; then
ESLINT_CONFIG_PASSING_FILE_CHANGED=1;
STYLELINT_CONFIG_FILE_CHANGED=1;
JAVASCRIPT_PACKAGES_CHANGED=1;
fi;
if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]] && [[ $FILE =~ ^core/modules/ckeditor5/js/build || $FILE =~ ^core/modules/ckeditor5/js/ckeditor5_plugins ]]; then
CKEDITOR5_PLUGINS_CHANGED=1;
fi;
if [[ $FILE == "core/misc/cspell/dictionary.txt" || $FILE == "core/misc/cspell/drupal-dictionary.txt" ]]; then
CSPELL_DICTIONARY_FILE_CHANGED=1;
fi
done
# Exit early if there are no files.
if [[ "$ABS_FILES" == "" ]]; then
printf "There are no files to check. If you have staged a commit use the --cached option.\n"
exit;
fi;
# This script assumes that composer install and yarn install have already been
# run and all dependencies are updated.
FINAL_STATUS=0
DEPENDENCIES_NEED_INSTALLING=0
# Ensure PHP development dependencies are installed.
# @todo https://github.com/composer/composer/issues/4497 Improve this to
# determine if dependencies in the lock file match the installed versions.
# Using composer install --dry-run is not valid because it would depend on
# user-facing strings in Composer.
if ! [[ -f 'vendor/bin/phpcs' ]]; then
printf "Drupal's PHP development dependencies are not installed. Run 'composer install' from the root directory.\n"
DEPENDENCIES_NEED_INSTALLING=1;
fi
cd "$TOP_LEVEL/core"
# Ensure JavaScript development dependencies are installed.
yarn check -s 2>/dev/null
if [ "$?" -ne "0" ]; then
printf "Drupal's JavaScript development dependencies are not installed or cannot be resolved. Run 'yarn install' inside the core directory, or 'yarn check -s' to list other errors.\n"
DEPENDENCIES_NEED_INSTALLING=1;
fi
if [ $DEPENDENCIES_NEED_INSTALLING -ne 0 ]; then
exit 1;
fi
# Check all files for spelling in one go for better performance.
if [[ $CSPELL_DICTIONARY_FILE_CHANGED == "1" ]] ; then
printf "\nRunning spellcheck on *all* files.\n"
yarn run -s spellcheck:core --no-must-find-files --no-progress
else
# Check all files for spelling in one go for better performance. We pipe the
# list files in so we obey the globs set on the spellcheck:core command in
# core/package.json.
echo "${ABS_FILES}" | tr ' ' '\n' | yarn run -s spellcheck:core --no-must-find-files --file-list stdin
fi
if [ "$?" -ne "0" ]; then
# If there are failures set the status to a number other than 0.
FINAL_STATUS=1
printf "\nCSpell: ${red}failed${reset}\n"
else
printf "\nCSpell: ${green}passed${reset}\n"
fi
cd "$TOP_LEVEL"
# Add a separator line to make the output easier to read.
printf "\n"
printf -- '-%.0s' {1..100}
printf "\n"
# Run PHPStan on all files on DrupalCI or when phpstan files are changed.
# APCu is disabled to ensure that the composer classmap is not corrupted.
if [[ $PHPSTAN_DIST_FILE_CHANGED == "1" ]] || [[ "$DRUPALCI" == "1" ]]; then
printf "\nRunning PHPStan on *all* files.\n"
php -d apc.enabled=0 -d apc.enable_cli=0 vendor/bin/phpstan analyze --no-progress --configuration="$TOP_LEVEL/core/phpstan.neon.dist"
else
# Only run PHPStan on changed files locally.
printf "\nRunning PHPStan on changed files.\n"
php -d apc.enabled=0 -d apc.enable_cli=0 vendor/bin/phpstan analyze --no-progress --configuration="$TOP_LEVEL/core/phpstan-partial.neon" $ABS_FILES
fi
if [ "$?" -ne "0" ]; then
# If there are failures set the status to a number other than 0.
FINAL_STATUS=1
printf "\nPHPStan: ${red}failed${reset}\n"
else
printf "\nPHPStan: ${green}passed${reset}\n"
fi
# Add a separator line to make the output easier to read.
printf "\n"
printf -- '-%.0s' {1..100}
printf "\n"
# Run PHPCS on all files on DrupalCI or when phpcs files are changed.
if [[ $PHPCS_XML_DIST_FILE_CHANGED == "1" ]] || [[ "$DRUPALCI" == "1" ]]; then
# Test all files with phpcs rules.
vendor/bin/phpcs -ps --parallel="$( (nproc || sysctl -n hw.logicalcpu || echo 4) 2>/dev/null)" --standard="$TOP_LEVEL/core/phpcs.xml.dist"
PHPCS=$?
if [ "$PHPCS" -ne "0" ]; then
# If there are failures set the status to a number other than 0.
FINAL_STATUS=1
printf "\nPHPCS: ${red}failed${reset}\n"
else
printf "\nPHPCS: ${green}passed${reset}\n"
fi
# Add a separator line to make the output easier to read.
printf "\n"
printf -- '-%.0s' {1..100}
printf "\n"
fi
# When the eslint config has been changed, then eslint must check all files.
if [[ $ESLINT_CONFIG_PASSING_FILE_CHANGED == "1" ]]; then
cd "$TOP_LEVEL/core"
yarn run -s lint:core-js-passing "$TOP_LEVEL/core"
CORRECTJS=$?
if [ "$CORRECTJS" -ne "0" ]; then
# If there are failures set the status to a number other than 0.
FINAL_STATUS=1
printf "\neslint: ${red}failed${reset}\n"
else
printf "\neslint: ${green}passed${reset}\n"
fi
cd $TOP_LEVEL
# Add a separator line to make the output easier to read.
printf "\n"
printf -- '-%.0s' {1..100}
printf "\n"
fi
# When the stylelint config has been changed, then stylelint must check all files.
if [[ $STYLELINT_CONFIG_FILE_CHANGED == "1" ]]; then
cd "$TOP_LEVEL/core"
yarn run -s lint:css
if [ "$?" -ne "0" ]; then
# If there are failures set the status to a number other than 0.
FINAL_STATUS=1
printf "\nstylelint: ${red}failed${reset}\n"
else
printf "\nstylelint: ${green}passed${reset}\n"
fi
cd $TOP_LEVEL
# Add a separator line to make the output easier to read.
printf "\n"
printf -- '-%.0s' {1..100}
printf "\n"
fi
# When a Drupal-specific CKEditor 5 plugin changed ensure that it is compiled
# properly. Only check on DrupalCI, since we're concerned about the build being
# run with the expected package versions and making sure the result of the build
# is in sync and conform to expectations.
if [[ "$DRUPALCI" == "1" ]] && [[ $CKEDITOR5_PLUGINS_CHANGED == "1" ]]; then
cd "$TOP_LEVEL/core"
yarn run -s check:ckeditor5
if [ "$?" -ne "0" ]; then
# If there are failures set the status to a number other than 0.
FINAL_STATUS=1
printf "\nDrupal-specific CKEditor 5 plugins: ${red}failed${reset}\n"
else
printf "\nDrupal-specific CKEditor 5 plugins: ${green}passed${reset}\n"
fi
cd $TOP_LEVEL
# Add a separator line to make the output easier to read.
printf "\n"
printf -- '-%.0s' {1..100}
printf "\n"
fi
# When JavaScript packages change, then rerun all JavaScript style checks.
if [[ "$JAVASCRIPT_PACKAGES_CHANGED" == "1" ]]; then
cd "$TOP_LEVEL/core"
yarn run build:css --check
CORRECTCSS=$?
if [ "$CORRECTCSS" -ne "0" ]; then
FINAL_STATUS=1
printf "\n${red}ERROR: The compiled CSS from the PCSS files"
printf "\n does not match the current CSS files. Some added"
printf "\n or updated JavaScript package made changes."
printf "\n Recompile the CSS with: yarn run build:css${reset}\n\n"
fi
cd $TOP_LEVEL
# Add a separator line to make the output easier to read.
printf "\n"
printf -- '-%.0s' {1..100}
printf "\n"
fi
for FILE in $FILES; do
STATUS=0;
# Print a line to separate spellcheck output from per file output.
printf "Checking %s\n" "$FILE"
printf "\n"
# Ensure the file still exists (i.e. is not being deleted).
if [ -a $FILE ]; then
if [ ${FILE: -3} != ".sh" ]; then
if [ -x $FILE ]; then
printf "${red}check failed:${reset} file $FILE should not be executable\n"
STATUS=1
fi
fi
fi
# Don't commit changes to vendor.
if [[ "$FILE" =~ ^vendor/ ]]; then
printf "${red}check failed:${reset} file in vendor directory being committed ($FILE)\n"
STATUS=1
fi
# Don't commit changes to core/node_modules.
if [[ "$FILE" =~ ^core/node_modules/ ]]; then
printf "${red}check failed:${reset} file in core/node_modules directory being committed ($FILE)\n"
STATUS=1
fi
############################################################################
### PHP AND YAML FILES
############################################################################
if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.(inc|install|module|php|profile|test|theme|yml)$ ]] && [[ $PHPCS_XML_DIST_FILE_CHANGED == "0" ]] && [[ "$DRUPALCI" == "0" ]]; then
# Test files with phpcs rules.
vendor/bin/phpcs "$TOP_LEVEL/$FILE" --standard="$TOP_LEVEL/core/phpcs.xml.dist"
PHPCS=$?
if [ "$PHPCS" -ne "0" ]; then
# If there are failures set the status to a number other than 0.
STATUS=1
else
printf "PHPCS: $FILE ${green}passed${reset}\n"
fi
fi
############################################################################
### YAML FILES
############################################################################
if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.yml$ ]]; then
# Test files with ESLint.
cd "$TOP_LEVEL/core"
node ./node_modules/eslint/bin/eslint.js --quiet --resolve-plugins-relative-to . "$TOP_LEVEL/$FILE"
YAMLLINT=$?
if [ "$YAMLLINT" -ne "0" ]; then
# If there are failures set the status to a number other than 0.
STATUS=1
else
printf "ESLint: $FILE ${green}passed${reset}\n"
fi
cd $TOP_LEVEL
fi
############################################################################
### JAVASCRIPT FILES
############################################################################
if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]]; then
cd "$TOP_LEVEL/core"
# Check the coding standards.
node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json "$TOP_LEVEL/$FILE"
JSLINT=$?
if [ "$JSLINT" -ne "0" ]; then
# No need to write any output the node command will do this for us.
STATUS=1
else
printf "ESLint: $FILE ${green}passed${reset}\n"
fi
cd $TOP_LEVEL
fi
############################################################################
### CSS FILES
############################################################################
if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.css$ ]]; then
# Work out the root name of the CSS so we can ensure that the PostCSS
# version has been compiled correctly.
if [[ $FILE =~ \.pcss\.css$ ]]; then
BASENAME=${FILE%.pcss.css}
COMPILE_CHECK=1
else
BASENAME=${FILE%.css}
# We only need to compile check if the .pcss.css file is not also
# changing. This is because the compile check will occur for the
# .pcss.css file. This might occur if the compiled stylesheets have
# changed.
contains_element "$BASENAME.pcss.css" "${FILES[@]}"
HASPOSTCSS=$?
if [ "$HASPOSTCSS" -ne "0" ]; then
COMPILE_CHECK=1
else
COMPILE_CHECK=0
fi
fi
# PostCSS
if [[ "$COMPILE_CHECK" == "1" ]] && [[ -f "$TOP_LEVEL/$BASENAME.pcss.css" ]]; then
cd "$TOP_LEVEL/core"
yarn run build:css --check --file "$TOP_LEVEL/$BASENAME.pcss.css"
CORRECTCSS=$?
if [ "$CORRECTCSS" -ne "0" ]; then
# If the CSS does not match the PCSS, set the status to a number other
# than 0.
STATUS=1
printf "\n${red}ERROR: The compiled CSS from"
printf "\n ${BASENAME}.pcss.css"
printf "\n does not match its CSS file. Recompile the CSS with:"
printf "\n yarn run build:css${reset}\n\n"
fi
cd $TOP_LEVEL
fi
fi
if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.css$ ]] && [[ -f "core/node_modules/.bin/stylelint" ]]; then
BASENAME=${FILE%.css}
# We only need to use stylelint on the .pcss.css file. So if this CSS file
# has a corresponding .pcss don't do stylelint.
if [[ $FILE =~ \.pcss\.css$ ]] || [[ ! -f "$TOP_LEVEL/$BASENAME.pcss.css" ]]; then
cd "$TOP_LEVEL/core"
node_modules/.bin/stylelint --allow-empty-input "$TOP_LEVEL/$FILE"
if [ "$?" -ne "0" ]; then
STATUS=1
else
printf "STYLELINT: $FILE ${green}passed${reset}\n"
fi
cd $TOP_LEVEL
fi
fi
if [[ "$STATUS" == "1" ]]; then
FINAL_STATUS=1
# There is no need to print a failure message. The fail will be described
# already.
else
printf "%s ${green}passed${reset}\n" "$FILE"
fi
# Print a line to separate each file's checks.
printf "\n"
printf -- '-%.0s' {1..100}
printf "\n"
done
if [[ "$FINAL_STATUS" == "1" ]] && [[ "$DRUPALCI" == "1" ]]; then
printf "${red}Drupal code quality checks failed.${reset}\n"
printf "To reproduce this output locally:\n"
printf "* Apply the change as a patch\n"
printf "* Run this command locally: sh ./core/scripts/dev/commit-code-check.sh\n"
printf "OR:\n"
printf "* From the merge request branch\n"
printf "* Run this command locally: sh ./core/scripts/dev/commit-code-check.sh --branch %s\n" "$DRUPAL_VERSION"
fi
exit $FINAL_STATUS

30
core/scripts/drupal Executable file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env php
<?php
/**
* @file
* Provides CLI commands for Drupal.
*/
use Drupal\Core\Command\GenerateTheme;
use Drupal\Core\Command\QuickStartCommand;
use Drupal\Core\Command\InstallCommand;
use Drupal\Core\Command\ServerCommand;
use Drupal\Core\Recipe\RecipeCommand;
use Symfony\Component\Console\Application;
if (PHP_SAPI !== 'cli') {
return;
}
$classloader = require_once __DIR__ . '/../../autoload.php';
$application = new Application('drupal', \Drupal::VERSION);
$application->add(new QuickStartCommand());
$application->add(new InstallCommand($classloader));
$application->add(new ServerCommand($classloader));
$application->add(new GenerateTheme());
$application->add(new RecipeCommand($classloader));
$application->run();

146
core/scripts/drupal.sh Executable file
View File

@@ -0,0 +1,146 @@
#!/usr/bin/env php
<?php
/**
* @file
* Drupal shell execution script
*
* Check for your PHP interpreter - on Windows you'll probably have to
* replace line 1 with
* #!c:/program files/php/php.exe
*
* @param path Drupal's absolute root directory in local file system (optional).
* @param URI A URI to execute, including HTTP protocol prefix.
*/
trigger_error('drupal.sh is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. There is no replacement. See https://www.drupal.org/node/3241346', E_USER_DEPRECATED);
$script = basename(array_shift($_SERVER['argv']));
if (in_array('--help', $_SERVER['argv']) || empty($_SERVER['argv'])) {
echo <<<EOF
Execute a Drupal page from the shell.
Usage: {$script} [OPTIONS] "<URI>"
Example: {$script} "http://my-site.org/node"
All arguments are long options.
--help This page.
--root Set the working directory for the script to the specified path.
To execute Drupal this has to be the root directory of your
Drupal installation, f.e. /home/www/foo/drupal (assuming Drupal
running on Unix). Current directory is not required.
Use surrounding quotation marks on Windows.
--verbose This option displays the options as they are set, but will
produce errors from setting the session.
URI The URI to execute, i.e. http://default/foo/bar for executing
the path '/foo/bar' in your site 'default'. URI has to be
enclosed by quotation marks if there are ampersands in it
(f.e. index.php?q=node&foo=bar). Prefix 'http://' is required,
and the domain must exist in Drupal's sites-directory.
If the given path and file exists it will be executed directly,
i.e. if URI is set to http://default/bar/foo.php
and bar/foo.php exists, this script will be executed without
bootstrapping Drupal. To execute Drupal's update.php, specify
http://default/update.php as the URI.
To run this script without --root argument invoke it from the root directory
of your Drupal installation with
./scripts/{$script}
\n
EOF;
exit;
}
$cmd = 'index.php';
// define default settings
$_SERVER['HTTP_HOST'] = 'default';
$_SERVER['PHP_SELF'] = '/index.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['SERVER_SOFTWARE'] = NULL;
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['QUERY_STRING'] = '';
$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/';
$_SERVER['HTTP_USER_AGENT'] = 'console';
// toggle verbose mode
if (in_array('--verbose', $_SERVER['argv'])) {
$_verbose_mode = TRUE;
}
else {
$_verbose_mode = FALSE;
}
// parse invocation arguments
while ($param = array_shift($_SERVER['argv'])) {
switch ($param) {
case '--root':
// change working directory
$path = array_shift($_SERVER['argv']);
if (is_dir($path)) {
chdir($path);
if ($_verbose_mode) {
echo "cwd changed to: {$path}\n";
}
}
else {
echo "\nERROR: {$path} not found.\n\n";
}
break;
default:
if (substr($param, 0, 2) == '--') {
// ignore unknown options
break;
}
else {
// parse the URI
$path = parse_url($param);
// set site name
if (isset($path['host'])) {
$_SERVER['HTTP_HOST'] = $path['host'];
}
// set query string
if (isset($path['query'])) {
$_SERVER['QUERY_STRING'] = $path['query'];
parse_str($path['query'], $_GET);
$_REQUEST = $_GET;
}
// set file to execute or Drupal path (clean URLs enabled)
if (isset($path['path']) && file_exists(substr($path['path'], 1))) {
$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = $path['path'];
$cmd = substr($path['path'], 1);
}
elseif (isset($path['path'])) {
$_SERVER['SCRIPT_NAME'] = '/' . $cmd;
$_SERVER['REQUEST_URI'] = $path['path'];
}
// display setup in verbose mode
if ($_verbose_mode) {
echo "Hostname set to: {$_SERVER['HTTP_HOST']}\n";
echo "Script name set to: {$cmd}\n";
echo "Path set to: {$path['path']}\n";
}
}
break;
}
}
if (file_exists($cmd)) {
include $cmd;
}
else {
echo "\nERROR: {$cmd} not found.\n\n";
}
exit();

106
core/scripts/dump-database-d6.sh Executable file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env php
<?php
/**
* Dump a Drupal 6 database into a Drupal 7 PHP script to test the upgrade
* process.
*
* Run this script at the root of an existing Drupal 6 installation.
*
* The output of this script is a PHP script that can be ran inside Drupal 7
* and recreates the Drupal 6 database as dumped. Transient data from cache
* session and watchdog tables are not recorded.
*/
// Define default settings.
$cmd = 'index.php';
$_SERVER['HTTP_HOST'] = 'default';
$_SERVER['PHP_SELF'] = '/index.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['SERVER_SOFTWARE'] = NULL;
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['QUERY_STRING'] = '';
$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/';
$_SERVER['HTTP_USER_AGENT'] = 'console';
// Bootstrap Drupal.
include_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// Include the utility drupal_var_export() function.
include_once __DIR__ . '/../includes/utility.inc';
// Output the PHP header.
$output = <<<END_OF_HEADER
<?php
/**
* @file
* Filled installation of Drupal 6.17, for test purposes.
*
* This file was generated by the dump-database-d6.sh tool, from an
* installation of Drupal 6, filled with data using the generate-d6-content.sh
* tool. It has the following modules installed:
END_OF_HEADER;
foreach (\Drupal::moduleHandler()->getModuleList() as $module => $filename) {
$output .= " * - $module\n";
}
$output .= " */\n\n";
// Get the current schema, order it by table name.
$schema = drupal_get_schema();
ksort($schema);
// Override the field type of the filename primary key to bypass the
// InnoDB 191 character limitation.
if (isset($schema['system']['primary key']) && $schema['system']['primary key'] == 'filename' && isset($schema['system']['fields']['filename']['type']) && $schema['system']['fields']['filename']['type'] == 'varchar') {
$schema['system']['fields']['filename']['type'] = 'varchar_ascii';
}
// Export all the tables in the schema.
foreach ($schema as $table => $data) {
// Remove descriptions to save time and code.
unset($data['description']);
foreach ($data['fields'] as &$field) {
unset($field['description']);
}
// Dump the table structure.
$output .= "db_create_table('" . $table . "', " . drupal_var_export($data) . ");\n";
// Don't output values for those tables.
if (substr($table, 0, 5) == 'cache' || $table == 'sessions' || $table == 'watchdog') {
$output .= "\n";
continue;
}
// Prepare the export of values.
$result = db_query('SELECT * FROM {'. $table .'}');
$insert = '';
while ($record = db_fetch_array($result)) {
// users.uid is a serial and inserting 0 into a serial can break MySQL.
// So record uid + 1 instead of uid for every uid and once all records
// are in place, fix them up.
if ($table == 'users') {
$record['uid']++;
}
$insert .= '->values('. drupal_var_export($record) .")\n";
}
// Dump the values if there are some.
if ($insert) {
$output .= "db_insert('". $table . "')->fields(". drupal_var_export(array_keys($data['fields'])) .")\n";
$output .= $insert;
$output .= "->execute();\n";
}
// Add the statement fixing the serial in the user table.
if ($table == 'users') {
$output .= "db_query('UPDATE {users} SET uid = uid - 1');\n";
}
$output .= "\n";
}
print $output;

View File

@@ -0,0 +1,91 @@
#!/usr/bin/env php
<?php
/**
* @file
* Dumps a Drupal 7 database into a Drupal 7 PHP script to test the upgrade
* process.
*
* Run this script at the root of an existing Drupal 7 installation.
*
* The output of this script is a PHP script that can be run inside Drupal 7
* and recreates the Drupal 7 database as dumped. Transient data from cache,
* session, and watchdog tables are not recorded.
*/
// Define default settings.
define('DRUPAL_ROOT', getcwd());
$cmd = 'index.php';
$_SERVER['HTTP_HOST'] = 'default';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['SERVER_SOFTWARE'] = NULL;
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['QUERY_STRING'] = '';
$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/';
$_SERVER['HTTP_USER_AGENT'] = 'console';
// Bootstrap Drupal.
include_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// Include the utility drupal_var_export() function.
include_once dirname(__FILE__) . '/../includes/utility.inc';
// Output the PHP header.
$output = <<<END_OF_HEADER
<?php
/**
* @file
* Filled installation of Drupal 7.0, for test purposes.
*
* This file was generated by the dump-database-d7.sh tool, from an
* installation of Drupal 7, filled with data using the generate-d7-content.sh
* tool. It has the following modules installed:
END_OF_HEADER;
foreach (\Drupal::moduleHandler()->getModuleList() as $module => $filename) {
$output .= " * - $module\n";
}
$output .= " */\n\n";
// Get the current schema, order it by table name.
$schema = drupal_get_schema();
ksort($schema);
// Export all the tables in the schema.
foreach ($schema as $table => $data) {
// Remove descriptions to save time and code.
unset($data['description']);
foreach ($data['fields'] as &$field) {
unset($field['description']);
}
// Dump the table structure.
$output .= "db_create_table('" . $table . "', " . drupal_var_export($data) . ");\n";
// Don't output values for those tables.
if (substr($table, 0, 5) == 'cache' || $table == 'sessions' || $table == 'watchdog') {
$output .= "\n";
continue;
}
// Prepare the export of values.
$result = db_query('SELECT * FROM {'. $table .'}', array(), array('fetch' => PDO::FETCH_ASSOC));
$insert = '';
foreach ($result as $record) {
$insert .= '->values('. drupal_var_export($record) .")\n";
}
// Dump the values if there are some.
if ($insert) {
$output .= "db_insert('". $table . "')->fields(". drupal_var_export(array_keys($data['fields'])) .")\n";
$output .= $insert;
$output .= "->execute();\n";
}
$output .= "\n";
}
print $output;

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env php
<?php
/**
* @file
* A command line application to dump a database to a generation script.
*/
use Drupal\Core\Command\DbDumpApplication;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\Request;
if (PHP_SAPI !== 'cli') {
return;
}
// Bootstrap.
$autoloader = require __DIR__ . '/../../autoload.php';
$request = Request::createFromGlobals();
Settings::initialize(dirname(__DIR__, 2), DrupalKernel::findSitePath($request), $autoloader);
DrupalKernel::createFromRequest($request, $autoloader, 'prod')->boot();
// Run the database dump command.
$application = new DbDumpApplication();
$application->run();

View File

@@ -0,0 +1,206 @@
#!/usr/bin/env php
<?php
/**
* Generate content for a Drupal 6 database to test the upgrade process.
*
* Run this script at the root of an existing Drupal 6 installation.
* Steps to use this generation script:
* - Install drupal 6.
* - Run this script from your Drupal ROOT directory.
* - Use the dump-database-d6.sh to generate the D7 file
* modules/simpletest/tests/upgrade/database.filled.php
*/
// Define settings.
$cmd = 'index.php';
$_SERVER['HTTP_HOST'] = 'default';
$_SERVER['PHP_SELF'] = '/index.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['SERVER_SOFTWARE'] = NULL;
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['QUERY_STRING'] = '';
$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/';
$_SERVER['HTTP_USER_AGENT'] = 'console';
$modules_to_enable = array('path', 'poll');
// Bootstrap Drupal.
include_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// Enable requested modules
include_once './modules/system/system.admin.inc';
$form = system_modules();
foreach ($modules_to_enable as $module) {
$form_state['values']['status'][$module] = TRUE;
}
$form_state['values']['disabled_modules'] = $form['disabled_modules'];
system_modules_submit(NULL, $form_state);
unset($form_state);
// Run cron after installing
drupal_cron_run();
// Create six users
for ($i = 0; $i < 6; $i++) {
$name = "test user $i";
$pass = md5("test PassW0rd $i !(.)");
$mail = "test$i@example.com";
$now = mktime(0, 0, 0, 1, $i + 1, 2010);
db_query("INSERT INTO {users} (name, pass, mail, status, created, access) VALUES ('%s', '%s', '%s', %d, %d, %d)", $name, $pass, $mail, 1, $now, $now);
}
// Create vocabularies and terms
$terms = array();
// All possible combinations of these vocabulary properties.
$hierarchy = array(0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2);
$multiple = array(0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1);
$required = array(0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
$voc_id = 0;
$term_id = 0;
for ($i = 0; $i < 24; $i++) {
$vocabulary = array();
++$voc_id;
$vocabulary['name'] = "vocabulary $voc_id (i=$i)";
$vocabulary['description'] = "description of ". $vocabulary['name'];
$vocabulary['nodes'] = $i > 11 ? array('page' => TRUE) : array();
$vocabulary['multiple'] = $multiple[$i % 12];
$vocabulary['required'] = $required[$i % 12];
$vocabulary['relations'] = 1;
$vocabulary['hierarchy'] = $hierarchy[$i % 12];
$vocabulary['weight'] = $i;
taxonomy_save_vocabulary($vocabulary);
$parents = array();
// Vocabularies without hierarchy get one term, single parent vocabularies get
// one parent and one child term. Multiple parent vocabularies get three
// terms: t0, t1, t2 where t0 is a parent of both t1 and t2.
for ($j = 0; $j < $vocabulary['hierarchy'] + 1; $j++) {
$term = array();
$term['vid'] = $vocabulary['vid'];
// For multiple parent vocabularies, omit the t0-t1 relation, otherwise
// every parent in the vocabulary is a parent.
$term['parent'] = $vocabulary['hierarchy'] == 2 && i == 1 ? array() : $parents;
++$term_id;
$term['name'] = "term $term_id of vocabulary $voc_id (j=$j)";
$term['description'] = 'description of ' . $term['name'];
$term['weight'] = $i * 3 + $j;
taxonomy_save_term($term);
$terms[] = $term['tid'];
$parents[] = $term['tid'];
}
}
$node_id = 0;
$revision_id = 0;
module_load_include('inc', 'node', 'node.pages');
for ($i = 0; $i < 24; $i++) {
$uid = intval($i / 8) + 3;
$user = user_load($uid);
$node = new stdClass();
$node->uid = $uid;
$node->type = $i < 12 ? 'page' : 'story';
$node->sticky = 0;
++$node_id;
++$revision_id;
$node->title = "node title $node_id rev $revision_id (i=$i)";
$type = node_get_types('type', $node->type);
if ($type->has_body) {
$node->body = str_repeat("node body ($node->type) - $i", 100);
$node->teaser = node_teaser($node->body);
$node->filter = variable_get('filter_default_format', 1);
$node->format = FILTER_FORMAT_DEFAULT;
}
$node->status = intval($i / 4) % 2;
$node->language = '';
$node->revision = $i < 12;
$node->promote = $i % 2;
$node->created = $now + $i * 86400;
$node->log = "added $i node";
// Make every term association different a little. For nodes with revisions,
// make the initial revision have a different set of terms than the
// newest revision.
$node_terms = $terms;
unset($node_terms[$i], $node_terms[47 - $i]);
if ($node->revision) {
$node->taxonomy = array($i => $terms[$i], 47-$i => $terms[47 - $i]);
}
else {
$node->taxonomy = $node_terms;
}
node_save($node);
path_set_alias("node/$node->nid", "content/$node->created");
if ($node->revision) {
$user = user_load($uid + 3);
++$revision_id;
$node->title .= " rev2 $revision_id";
$node->body = str_repeat("node revision body ($node->type) - $i", 100);
$node->log = "added $i revision";
$node->taxonomy = $node_terms;
node_save($node);
}
}
// Create poll content
for ($i = 0; $i < 12; $i++) {
$uid = intval($i / 4) + 3;
$user = user_load($uid);
$node = new stdClass();
$node->uid = $uid;
$node->type = 'poll';
$node->sticky = 0;
$node->title = "poll title $i";
$type = node_get_types('type', $node->type);
if ($type->has_body) {
$node->body = str_repeat("node body ($node->type) - $i", 100);
$node->teaser = node_teaser($node->body);
$node->filter = variable_get('filter_default_format', 1);
$node->format = FILTER_FORMAT_DEFAULT;
}
$node->status = intval($i / 2) % 2;
$node->language = '';
$node->revision = 1;
$node->promote = $i % 2;
$node->created = $now + $i * 43200;
$node->log = "added $i poll";
$number_of_choices = ($i % 4) + 2;
for ($c = 0; $c < $number_of_choices; $c++) {
$node->choice[] = array('chtext' => "Choice $c for poll $i");
}
node_save($node);
path_set_alias("node/$node->nid", "content/poll/$i");
path_set_alias("node/$node->nid/results", "content/poll/$i/results");
// Add some votes
for ($v = 0; $v < ($i % 4) + 5; $v++) {
$c = $v % $number_of_choices;
$form_state = array();
$form_state['values']['choice'] = $c;
$form_state['values']['op'] = t('Vote');
drupal_execute('poll_view_voting', $form_state, $node);
}
}
$uid = 6;
$user = user_load($uid);
$node = new stdClass();
$node->uid = $uid;
$node->type = 'broken';
$node->sticky = 0;
$node->title = "node title 24";
$node->body = str_repeat("node body ($node->type) - 37", 100);
$node->teaser = node_teaser($node->body);
$node->filter = variable_get('filter_default_format', 1);
$node->format = FILTER_FORMAT_DEFAULT;
$node->status = 1;
$node->language = '';
$node->revision = 0;
$node->promote = 0;
$node->created = 1263769200;
$node->log = "added $i node";
node_save($node);
path_set_alias("node/$node->nid", "content/1263769200");

View File

@@ -0,0 +1,318 @@
#!/usr/bin/env php
<?php
/**
* Generate content for a Drupal 7 database to test the upgrade process.
*
* Run this script at the root of an existing Drupal 7 installation.
*
* Steps to use this generation script:
* - Install drupal 7.
* - Run this script from your Drupal ROOT directory.
* - Use the dump-database-d7.sh to generate the D7 file
* modules/simpletest/tests/upgrade/database.filled.php
*/
// Define settings.
$cmd = 'index.php';
define('DRUPAL_ROOT', getcwd());
$_SERVER['HTTP_HOST'] = 'default';
$_SERVER['PHP_SELF'] = '/index.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['SERVER_SOFTWARE'] = NULL;
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['QUERY_STRING'] = '';
$_SERVER['PHP_SELF'] = $_SERVER['REQUEST_URI'] = '/';
$_SERVER['HTTP_USER_AGENT'] = 'console';
$modules_to_enable = array('path', 'poll', 'taxonomy');
// Bootstrap Drupal.
include_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// Enable requested modules
require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
include_once './modules/system/system.admin.inc';
$form = system_modules();
foreach ($modules_to_enable as $module) {
$form_state['values']['status'][$module] = TRUE;
}
$form_state['values']['disabled_modules'] = $form['disabled_modules'];
system_modules_submit(NULL, $form_state);
unset($form_state);
// Run cron after installing
drupal_cron_run();
// Create six users
$query = db_insert('users')->fields(array('uid', 'name', 'pass', 'mail', 'status', 'created', 'access'));
for ($i = 0; $i < 6; $i++) {
$name = "test user $i";
$pass = md5("test PassW0rd $i !(.)");
$mail = "test$i@example.com";
$now = mktime(0, 0, 0, 1, $i + 1, 2010);
$query->values(array(db_next_id(), $name, user_hash_password($pass), $mail, 1, $now, $now));
}
$query->execute();
// Create vocabularies and terms
$terms = array();
// All possible combinations of these vocabulary properties.
$hierarchy = array(0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2);
$multiple = array(0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1);
$required = array(0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1);
$voc_id = 0;
$term_id = 0;
for ($i = 0; $i < 24; $i++) {
$vocabulary = new stdClass;
++$voc_id;
$vocabulary->name = "vocabulary $voc_id (i=$i)";
$vocabulary->machine_name = 'vocabulary_' . $voc_id . '_' . $i;
$vocabulary->description = "description of ". $vocabulary->name;
$vocabulary->multiple = $multiple[$i % 12];
$vocabulary->required = $required[$i % 12];
$vocabulary->relations = 1;
$vocabulary->hierarchy = $hierarchy[$i % 12];
$vocabulary->weight = $i;
taxonomy_vocabulary_save($vocabulary);
$field = array(
'field_name' => 'taxonomy_'. $vocabulary->machine_name,
'module' => 'taxonomy',
'type' => 'taxonomy_term_reference',
'cardinality' => $vocabulary->multiple || $vocabulary->tags ? FIELD_CARDINALITY_UNLIMITED : 1,
'settings' => array(
'required' => $vocabulary->required ? TRUE : FALSE,
'allowed_values' => array(
array(
'vocabulary' => $vocabulary->machine_name,
'parent' => 0,
),
),
),
);
field_create_field($field);
$node_types = $i > 11 ? array('page') : array_keys(node_type_get_types());
foreach ($node_types as $bundle) {
$instance = array(
'label' => $vocabulary->name,
'field_name' => $field['field_name'],
'bundle' => $bundle,
'entity_type' => 'node',
'settings' => array(),
'description' => $vocabulary->help,
'required' => $vocabulary->required,
'widget' => array(),
'display' => array(
'default' => array(
'type' => 'taxonomy_term_reference_link',
'weight' => 10,
),
'teaser' => array(
'type' => 'taxonomy_term_reference_link',
'weight' => 10,
),
),
);
if ($vocabulary->tags) {
$instance['widget'] = array(
'type' => 'taxonomy_autocomplete',
'module' => 'taxonomy',
'settings' => array(
'size' => 60,
'autocomplete_path' => 'taxonomy/autocomplete',
),
);
}
else {
$instance['widget'] = array(
'type' => 'options_select',
'settings' => array(),
);
}
field_create_instance($instance);
}
$parents = array();
// Vocabularies without hierarchy get one term, single parent vocabularies get
// one parent and one child term. Multiple parent vocabularies get three
// terms: t0, t1, t2 where t0 is a parent of both t1 and t2.
for ($j = 0; $j < $vocabulary->hierarchy + 1; $j++) {
++$term_id;
$term = entity_create('taxonomy_term', array(
'vocabulary_machine_name' => $vocabulary->machine_name,
// For multiple parent vocabularies, omit the t0-t1 relation, otherwise
// every parent in the vocabulary is a parent.
'parent' => $vocabulary->hierarchy == 2 && i == 1 ? array() : $parents,
'name' => "term $term_id of vocabulary $voc_id (j=$j)",
'description' => 'description of ' . $term->name,
'format' => 'filtered_html',
'weight' => $i * 3 + $j,
));
taxonomy_term_save($term);
$terms[] = $term->tid;
$term_vocabs[$term->tid] = 'taxonomy_' . $vocabulary->machine_name;
$parents[] = $term->tid;
}
}
$node_id = 0;
$revision_id = 0;
module_load_include('inc', 'node', 'node.pages');
for ($i = 0; $i < 36; $i++) {
$uid = intval($i / 8) + 3;
$user = user_load($uid);
$node = new stdClass();
$node->uid = $uid;
$node->type = 'page';
if ($i < 12) {
$node->type = 'page';
}
elseif ($i < 24) {
$node->type = 'story';
}
elseif (module_exists('blog')) {
$node->type = 'blog';
}
$node->sticky = 0;
++$node_id;
++$revision_id;
$node->title = "node title $node_id rev $revision_id (i=$i)";
$node->language = LANGUAGE_NONE;
$body_text = str_repeat("node body ($node->type) - $i", 100);
$node->body[$node->language][0]['value'] = $body_text;
$node->body[$node->language][0]['summary'] = text_summary($body_text);
$node->body[$node->language][0]['format'] = 'filtered_html';
$node->status = intval($i / 4) % 2;
$node->revision = $i < 12;
$node->promote = $i % 2;
$node->created = $now + $i * 86400;
$node->log = "added $i node";
// Make every term association different a little. For nodes with revisions,
// make the initial revision have a different set of terms than the
// newest revision.
$items = array();
if ($node->revision) {
$node_terms = array($terms[$i], $terms[47-$i]);
}
else {
$node_terms = $terms;
unset($node_terms[$i], $node_terms[47 - $i]);
}
foreach ($node_terms as $tid) {
$field_name = $term_vocabs[$tid];
$node->{$field_name}[LANGUAGE_NONE][] = array('tid' => $tid);
}
$node->path = array('alias' => "content/$node->created");
node_save($node);
if ($node->revision) {
$user = user_load($uid + 3);
++$revision_id;
$node->title .= " rev2 $revision_id";
$body_text = str_repeat("node revision body ($node->type) - $i", 100);
$node->body[$node->language][0]['value'] = $body_text;
$node->body[$node->language][0]['summary'] = text_summary($body_text);
$node->body[$node->language][0]['format'] = 'filtered_html';
$node->log = "added $i revision";
$node_terms = $terms;
unset($node_terms[$i], $node_terms[47 - $i]);
foreach ($node_terms as $tid) {
$field_name = $term_vocabs[$tid];
$node->{$field_name}[LANGUAGE_NONE][] = array('tid' => $tid);
}
node_save($node);
}
}
// Create poll content
for ($i = 0; $i < 12; $i++) {
$uid = intval($i / 4) + 3;
$user = user_load($uid);
$node = new stdClass();
$node->uid = $uid;
$node->type = 'poll';
$node->sticky = 0;
$node->title = "poll title $i";
$node->language = LANGUAGE_NONE;
$node->status = intval($i / 2) % 2;
$node->revision = 1;
$node->promote = $i % 2;
$node->created = $_SERVER['REQUEST_TIME'] + $i * 43200;
$node->runtime = 0;
$node->active = 1;
$node->log = "added $i poll";
$node->path = array('alias' => "content/poll/$i");
$number_of_choices = ($i % 4) + 2;
for ($c = 0; $c < $number_of_choices; $c++) {
$node->choice[] = array('chtext' => "Choice $c for poll $i", 'chvotes' => 0, 'weight' => 0);
}
node_save($node);
$path = array(
'alias' => "content/poll/$i/results",
'source' => "node/$node->nid/results",
);
path_save($path);
// Add some votes
$node = node_load($node->nid);
$choices = array_keys($node->choice);
$original_user = $GLOBALS['user'];
for ($v = 0; $v < ($i % 4); $v++) {
drupal_static_reset('ip_address');
$_SERVER['REMOTE_ADDR'] = "127.0.$v.1";
$GLOBALS['user'] = drupal_anonymous_user();// We should have already allowed anon to vote.
$c = $v % $number_of_choices;
$form_state = array();
$form_state['values']['choice'] = $choices[$c];
$form_state['values']['op'] = t('Vote');
drupal_form_submit('poll_view_voting', $form_state, $node);
}
}
$uid = 6;
$node_type = 'broken';
$user = user_load($uid);
$node = new stdClass();
$node->uid = $uid;
$node->type = 'article';
$body_text = str_repeat("node body ($node_type) - 37", 100);
$node->sticky = 0;
$node->title = "node title 24";
$node->language = LANGUAGE_NONE;
$node->body[$node->language][0]['value'] = $body_text;
$node->body[$node->language][0]['summary'] = text_summary($body_text);
$node->body[$node->language][0]['format'] = 'filtered_html';
$node->status = 1;
$node->revision = 0;
$node->promote = 0;
$node->created = 1263769200;
$node->log = "added a broken node";
$node->path = array('alias' => "content/1263769200");
node_save($node);
db_update('node')
->fields(array(
'type' => $node_type,
))
->condition('nid', $node->nid)
->execute();
db_update('field_data_body')
->fields(array(
'bundle' => $node_type,
))
->condition('entity_id', $node->nid)
->condition('entity_type', 'node')
->execute();
db_update('field_revision_body')
->fields(array(
'bundle' => $node_type,
))
->condition('entity_id', $node->nid)
->condition('entity_type', 'node')
->execute();
db_update('field_config_instance')
->fields(array(
'bundle' => $node_type,
))
->condition('bundle', 'article')
->execute();

View File

@@ -0,0 +1,34 @@
#!/usr/bin/env php
<?php
/**
* @file
* A script to generate proxy classes for lazy services.
*
* For help, type this command from the root directory of an installed Drupal
* site: php core/scripts/generate-proxy-class.php -h generate-proxy-class
*
* @ingroup container
*
* @see lazy_services
*/
use Drupal\Core\Command\GenerateProxyClassApplication;
use Drupal\Core\DrupalKernel;
use Drupal\Core\ProxyBuilder\ProxyBuilder;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\Request;
if (PHP_SAPI !== 'cli') {
return;
}
// Bootstrap.
$autoloader = require __DIR__ . '/../../autoload.php';
$request = Request::createFromGlobals();
Settings::initialize(dirname(__DIR__, 2), DrupalKernel::findSitePath($request), $autoloader);
DrupalKernel::createFromRequest($request, $autoloader, 'prod')->boot();
// Run the database dump command.
$application = new GenerateProxyClassApplication(new ProxyBuilder());
$application->run();

5
core/scripts/js/.eslintrc.json Executable file
View File

@@ -0,0 +1,5 @@
{
"rules": {
"strict": [2, "global"]
}
}

View File

@@ -0,0 +1,65 @@
/**
* @file
* Callback returning the list of files to copy to the assets/vendor directory.
*/
const { globSync } = require('glob');
// There are a lot of CKEditor 5 packages, generate the list dynamically.
// Drupal-specific mapping between CKEditor 5 name and Drupal library name.
const ckeditor5PluginMapping = {
'block-quote': 'blockquote',
essentials: 'internal',
'basic-styles': 'basic',
};
/**
* Build the list of assets to be copied based on what exists in the filesystem.
*
* @param {string} packageFolder
* The path to node_modules folder.
*
* @return {DrupalLibraryAsset[]}
* List of libraries and files to process.
*/
module.exports = (packageFolder) => {
const fileList = [];
// Get all the CKEditor 5 packages.
const ckeditor5Dirs = globSync(`{${packageFolder}/@ckeditor/ckeditor5*,${packageFolder}/ckeditor5}`).sort();
for (const ckeditor5package of ckeditor5Dirs) {
// Add all the files in the build/ directory to the process array for
// copying.
const buildFiles = globSync(`${ckeditor5package}/build/**/*.js`, {
nodir: true,
});
if (buildFiles.length) {
// Clean up the path to get the original package name.
const pack = ckeditor5package.replace(`${packageFolder}/`, '');
// Use the package name to generate the plugin name. There are some
// exceptions that needs to be handled. Ideally remove the special cases.
let pluginName = pack.replace('@ckeditor/ckeditor5-', '');
// Target folder in the vendor/assets folder.
let folder = `ckeditor5/${pluginName.replace('@ckeditor/ckeditor5-', '')}`;
// Transform kebab-case to CamelCase.
let library = pluginName.replace(/-./g, (match) => match[1].toUpperCase());
// Special case for Drupal implementation.
if (ckeditor5PluginMapping.hasOwnProperty(pluginName)) {
library = ckeditor5PluginMapping[pluginName];
}
if (library === 'ckeditor5') {
folder = 'ckeditor5/ckeditor5-dll';
} else {
library = `ckeditor5.${library}`;
}
fileList.push({
pack,
library,
folder,
files: buildFiles.map((absolutePath) => ({
from: absolutePath.replace(`${ckeditor5package}/`, ''),
to: absolutePath.replace(`${ckeditor5package}/build/`, ''),
})),
});
}
}
return fileList;
};

47
core/scripts/js/assets/process/jqueryui.js vendored Executable file
View File

@@ -0,0 +1,47 @@
const Terser = require('terser');
const path = require('path');
/**
* Process jQuery UI source files.
*
* Each file being processed creates 3 files under assets/vendor/jquery.ui/:
* - The original source for audit purposes, with a `.js` suffix.
* - The minified version for production use, with a `-min.js` suffix.
* - The source map for debugging purposes, with a `-min.js.map` suffix.
*
* @param {object} data
* Object passed to the callback.
* @param {object} data.file
* Normalized file information object.
* @param {string} data.file.from
* Path of the file in node_modules/ directory.
* @param {string} data.file.to
* Path of the file in core assets/vendor/ directory.
* @param {string} data.contents
* Content of the file being processed.
*
* @return {Promise<[{filename: string, contents: string}]>}
* Return a Promise that resolves into an array of file and content to create
* in the assets/vendor/ directory.
*/
module.exports = async ({ file: { from, to }, contents }) => {
const filename = `${to.slice(0, -3)}-min.js`;
const sourcemap = `${filename}.map`;
const { code, map } = await Terser.minify(
{ [path.basename(from)]: contents }, {
sourceMap: {
filename: path.basename(filename),
url: path.basename(sourcemap),
},
});
return [
// Original file.
{ filename: to, contents },
// Minified file.
{ filename, contents: code },
// Sourcemap file.
{ filename: sourcemap, contents: map },
];
};

View File

@@ -0,0 +1,20 @@
/**
* Process map files.
*
* In the `sources` member, remove all "../" values at the start of the file
* names to avoid virtual files located outside of the library vendor folder.
*
* @param {object} data
* Object passed to the callback.
* @param {string} data.contents
* Content of the file being processed.
*
* @return {Promise<[{contents: string}]>}
* Return a Promise that resolves into an array of file and content to create
* in the assets/vendor/ directory.
*/
module.exports = ({ contents }) => {
const json = JSON.parse(contents);
json.sources = json.sources.map((source) => source.replace(/^(\.\.\/)+/, ''));
return [{ contents: JSON.stringify(json) }];
};

View File

@@ -0,0 +1,39 @@
/**
* @file
*
* Provides the `check:ckeditor5` command.
*
* Check that the plugins are built with the appropriate dependencies. This is
* only run on DrupalCI.
*
* @internal This file is part of the core JavaScript build process and is only
* meant to be used in that context.
*/
"use strict";
const { globSync } = require("glob");
const log = require("./log");
const fs = require("fs").promises;
const child_process = require("child_process");
async function getContents(files) {
return Object.fromEntries(
await Promise.all(
files.map(async (file) => [file, (await fs.readFile(file)).toString()])
)
);
}
(async () => {
const files = globSync("./modules/ckeditor5/js/build/*.js").sort();
const pluginsBefore = await getContents(files);
// Execute the plugin build script.
child_process.execSync("yarn run build:ckeditor5");
const pluginsAfter = await getContents(files);
if (JSON.stringify(pluginsBefore) !== JSON.stringify(pluginsAfter)) {
process.exitCode = 1;
}
})();

View File

@@ -0,0 +1,117 @@
/**
* @file
*
* Provides the `build:ckeditor5-types` command.
*
* This command is used for generating documentation for mapping CKEditor 5
* types so that they can be parsed by IDEs.
*
* @internal This file is part of the core javascript build process and is only
* meant to be used in that context.
*/
'use strict';
const { globSync } = require('glob');
const log = require('./log');
const fs = require('fs');
/**
* A list of regex used to alias CKEditor 5 types.
*
* @type {RegExp[]}
*/
const regexList = [
// Makes sure that `export default class` code can be referenced with the
// class name and not the module name only.
/ * @module \b(.*)\b[\s\S]*?export default(?: class| function)? \b(\w+)\b/g,
// Pick up CKEditor 5 own aliases to alias them too.
/ * @module \b(.*)\b[\s\S]*?@(?:typedef|interface) (?:.*~)?(\w+)/g,
];
const globOptions = {
// Search within the ckeditor npm namespace.
cwd: process.cwd() + '/node_modules/@ckeditor/',
absolute: true,
};
/**
* Template for the generated typedef comment.
*
* @param {string} file
* The path to the file containing the type definition.
* @param {string} module
* The module name as defined by the @module jsdoc comment.
* @param {string} name
* The name of the class being exported
*
* @return {string}
* The comment aliasing the module name to the specific named exports.
*/
function generateTypeDef(file, module, name) {
const cleanModule = module.replace('module:', '');
return `/**
* Declared in file @ckeditor/${file.replace(globOptions.cwd, '')}
*
* @typedef {module:${cleanModule}} module:${cleanModule}~${name}
*/
`;
}
/**
* Helper to get the file contents as a string.
*
* @param {string} filePath
* Absolute path to the file.
*
* @return {string}
*/
function getFile(filePath) {
try {
return fs.readFileSync(filePath, 'utf8');
} catch (err) {
return '';
}
}
/**
* Returns a callback function.
*
* @param {string} filePath
* The CKEditor 5 source file to inspect for exports or type definitions.
*
* @return {function}
* The aliased typedef string.
*
* @see generateTypeDef
*/
function processFile(filePath) {
const fileData = getFile(filePath);
// Use a for loop to be able to return early.
for (const regex of regexList) {
// Reset the match index of the Regex to make sure we search from the
// beginning of the file every time.
regex.lastIndex = 0;
const m = regex.exec(fileData);
if (m) {
return generateTypeDef(filePath, m[1], m[2]);
}
}
return false;
}
const definitions = globSync('./ckeditor5*/src/**/*.+(js|jsdoc)', globOptions).sort().map(processFile);
// Filter definitions that do not match any regex.
const existingDefinitions = definitions.filter((e) => !!e);
// Write the file in the ckeditor module, use the JSDoc extension to make sure
// the JSDoc extension is associated with the JavaScript file type and it
// prevents core JavaScript lint rules to be run. Add it to the build folder to
// prevent cspell checks on this file.
fs.writeFile(`./modules/ckeditor5/js/build/ckeditor5.types.jsdoc`, existingDefinitions.join('\n'), () => {
log(`CKEditor 5 types have been generated: ${existingDefinitions.length} declarations aliased, ${definitions.length - existingDefinitions.length} files ignored`);
});
process.exitCode = 0;

View File

@@ -0,0 +1,35 @@
module.exports = function (results) {
results = results || [];
const errorType = {
warnings: {},
errors: {},
};
results.reduce((result, current) => {
current.messages.forEach((msg) => {
if (msg.severity === 1) {
errorType.warnings[msg.ruleId] = errorType.warnings[msg.ruleId] + 1 || 1
}
if (msg.severity === 2) {
errorType.errors[msg.ruleId] = errorType.errors[msg.ruleId] + 1 || 1
}
});
return result;
});
const reduceErrorCounts = (errorType) => Object.entries(errorType).sort((a, b) => b[1] - a[1])
.reduce((result, current) => result.concat([`${current[0]}: ${current[1]}`]), []).join('\n');
const warnings = reduceErrorCounts(errorType.warnings);
const errors = reduceErrorCounts(errorType.errors);
return `
Errors:
${'='.repeat(30)}
${errors}
${'\n'.repeat(4)}
Warnings:
${'='.repeat(30)}
${warnings}`;
};

4
core/scripts/js/log.js Executable file
View File

@@ -0,0 +1,4 @@
module.exports = (message) => {
// Logging human-readable timestamp.
console.log(`[${new Date().toTimeString().slice(0, 8)}] ${message}`);
}

278
core/scripts/js/vendor-update.js Executable file
View File

@@ -0,0 +1,278 @@
/**
* @file
* Copy files for JS vendor dependencies from node_modules to the assets/vendor
* folder.
*
* This script handles all dependencies except CKEditor and Modernizr, which
* require a custom build step.
*/
const path = require('path');
const { copyFile, writeFile, readFile, chmod, mkdir } = require('fs').promises;
const ckeditor5Files = require('./assets/ckeditor5Files');
const jQueryUIProcess = require('./assets/process/jqueryui');
const mapProcess = require('./assets/process/map');
const coreFolder = path.resolve(__dirname, '../../');
const packageFolder = `${coreFolder}/node_modules`;
const assetsFolder = `${coreFolder}/assets/vendor`;
(async () => {
const librariesPath = `${coreFolder}/core.libraries.yml`;
// Open the core.libraries.yml file to update version information
// automatically.
const libraries = (await readFile(librariesPath)).toString().split('\n\n');
function updateLibraryVersion(libraryName, { version }) {
const libraryIndex = libraries.findIndex((lib) =>
lib.startsWith(libraryName),
);
if (libraryIndex > 0) {
const libraryDeclaration = libraries[libraryIndex];
// Get the previous package version from the yaml file, versions can be
// declared with a yaml anchor such as `version: &yaml_anchor "xxx"`
const currentVersion = libraryDeclaration.match(/version:(?: [&\w_]+)? "(.*)"\n/)[1];
// Replace the version value and the version in the license URL.
libraries[libraryIndex] = libraryDeclaration.replace(
new RegExp(currentVersion, 'g'),
version,
);
}
}
/**
* Structure of the object defining a library to copy to the assets/ folder.
*
* @typedef DrupalLibraryAsset
*
* @prop {string} pack
* The name of the npm package (used to get the name of the folder where
* the files are situated inside of the node_modules folder). Note that we
* use `pack` and not `package` because `package` is a future reserved word.
* @prop {string} [folder]
* The folder under `assets/vendor/` where the files will be copied. If
* this
* is not defined the value of `pack` is used.
* @prop {string} [library]
* The key under which the library is declared in core.libraries.yml.
* @prop {Array} [files]
* An array of files to be copied over.
* - A string if the file has the same name and is at the same level in
* the source and target folder.
* - An object with a `from` and `to` property if the source and target
* have a different name or if the folder nesting is different.
* @prop {object} [process]
* An object containing a file extension as a key and a callback as the
* value. The callback will be called for each file matching the file
* extension. It can be used to minify the file content before saving to
* the target directory.
*/
/**
* Declare the array that defines what needs to be copied over.
*
* @type {DrupalLibraryAsset[]}
*/
const ASSET_LIST = [
{
pack: 'backbone',
library: 'internal.backbone',
files: ['backbone.js', 'backbone-min.js', 'backbone-min.js.map'],
},
{
pack: 'jquery',
files: [
{ from: 'dist/jquery.js', to: 'jquery.js' },
{ from: 'dist/jquery.min.js', to: 'jquery.min.js' },
{ from: 'dist/jquery.min.map', to: 'jquery.min.map' },
],
},
{
pack: 'js-cookie',
files: [{ from: 'dist/js.cookie.min.js', to: 'js.cookie.min.js' }],
},
{
pack: 'normalize.css',
folder: 'normalize-css',
library: 'normalize',
files: ['normalize.css'],
},
{
pack: '@drupal/once',
folder: 'once',
files: [
{ from: 'dist/once.js', to: 'once.js' },
{ from: 'dist/once.min.js', to: 'once.min.js' },
{ from: 'dist/once.min.js.map', to: 'once.min.js.map' },
],
},
{
pack: 'shepherd.js',
folder: 'shepherd',
library: 'internal.shepherd',
files: [
{ from: 'dist/js/shepherd.min.js', to: 'shepherd.min.js' },
{ from: 'dist/js/shepherd.min.js.map', to: 'shepherd.min.js.map' },
],
},
{ pack: 'sortablejs', folder: 'sortable', files: ['Sortable.min.js'] },
{
pack: 'tabbable',
files: [
{ from: 'dist/index.umd.min.js', to: 'index.umd.min.js' },
{ from: 'dist/index.umd.min.js.map', to: 'index.umd.min.js.map' },
],
},
{
pack: 'underscore',
library: 'internal.underscore',
files: ['underscore-min.js', 'underscore-min.js.map'],
},
{
pack: 'loadjs',
files: [{ from: 'dist/loadjs.min.js', to: 'loadjs.min.js' }],
},
{
pack: 'tua-body-scroll-lock',
files: [
{ from: 'dist/tua-bsl.umd.min.js', to: 'tua-bsl.umd.min.js' },
],
},
{
pack: 'transliteration',
files: [
{ from: 'dist/browser/bundle.umd.min.js', to: 'bundle.umd.min.js' },
{ from: 'dist/browser/bundle.umd.min.js.map', to: 'bundle.umd.min.js.map' },
],
},
{
pack: 'jquery-ui',
folder: 'jquery.ui',
process: {
// This will automatically minify the files and update the destination
// filename before saving.
'.js': jQueryUIProcess,
},
files: [
'themes/base/autocomplete.css',
'themes/base/button.css',
'themes/base/checkboxradio.css',
'themes/base/controlgroup.css',
'themes/base/core.css',
'themes/base/dialog.css',
'themes/base/draggable.css',
'themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png',
'themes/base/images/ui-icons_444444_256x240.png',
'themes/base/images/ui-icons_555555_256x240.png',
'themes/base/images/ui-icons_777620_256x240.png',
'themes/base/images/ui-icons_777777_256x240.png',
'themes/base/images/ui-icons_cc0000_256x240.png',
'themes/base/images/ui-icons_ffffff_256x240.png',
'themes/base/menu.css',
'themes/base/resizable.css',
'themes/base/theme.css',
'ui/data.js',
'ui/disable-selection.js',
'ui/focusable.js',
'ui/form-reset-mixin.js',
'ui/form.js',
'ui/ie.js',
'ui/jquery-patch.js',
'ui/keycode.js',
'ui/labels.js',
'ui/plugin.js',
'ui/safe-active-element.js',
'ui/safe-blur.js',
'ui/scroll-parent.js',
'ui/unique-id.js',
'ui/version.js',
'ui/widget.js',
'ui/widgets/autocomplete.js',
'ui/widgets/button.js',
'ui/widgets/checkboxradio.js',
'ui/widgets/controlgroup.js',
'ui/widgets/dialog.js',
'ui/widgets/draggable.js',
'ui/widgets/menu.js',
'ui/widgets/mouse.js',
'ui/widgets/resizable.js',
],
},
// CKEditor 5 builds the list of files dynamically based on what exists
// in the filesystem.
...ckeditor5Files(packageFolder),
];
/**
* Default callback for processing map files.
*/
const defaultProcessCallbacks = {
'.map': mapProcess,
};
/**
* Return an object with a 'from' and 'to' member.
*
* @param {string|object} file
*
* @return {{from: string, to: string}}
*/
function normalizeFile(file) {
let normalized = file;
if (typeof file === 'string') {
normalized = {
from: file,
to: file,
};
}
return normalized;
}
for (const { pack, files = [], folder = false, library = false, process = {} } of ASSET_LIST) {
const sourceFolder = pack;
const libraryName = library || folder || pack;
const destFolder = folder || pack;
// Add a callback for map files by default.
const processCallbacks = { ...defaultProcessCallbacks, ...process };
// Update the library version in core.libraries.yml with the version
// from the npm package.
try {
const packageInfo = JSON.parse((await readFile(`${packageFolder}/${sourceFolder}/package.json`)).toString());
updateLibraryVersion(libraryName, packageInfo);
} catch (e) {
// The package.json file doesn't exist, so nothing to do.
}
for (const file of files.map(normalizeFile)) {
const sourceFile = `${packageFolder}/${sourceFolder}/${file.from}`;
const destFile = `${assetsFolder}/${destFolder}/${file.to}`;
const extension = path.extname(file.from);
try {
await mkdir(path.dirname(destFile), { recursive: true });
} catch (e) {
// Nothing to do if the folder already exists.
}
// There is a callback that transforms the file contents, we are not
// simply copying a file from A to B.
if (processCallbacks[extension]) {
const contents = (await readFile(sourceFile)).toString();
const results = await processCallbacks[extension]({ file, contents });
console.log(`Process ${sourceFolder}/${file.from} and save ${results.length} files:\n ${results.map(({ filename = file.to }) => filename).join(', ')}`);
for (const { filename = file.to, contents } of results) {
// The filename key can be used to change the name of the saved file.
await writeFile(`${assetsFolder}/${destFolder}/${filename}`, contents);
}
} else {
// There is no callback simply copy the file.
console.log(`Copy ${sourceFolder}/${file.from} to ${destFolder}/${file.to}`);
await copyFile(sourceFile, destFile);
}
}
}
await writeFile(librariesPath, libraries.join('\n\n'));
})();

61
core/scripts/password-hash.sh Executable file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env php
<?php
/**
* @file
* Drupal hash script - to generate a hash from a plaintext password
*
* @param password1 [password2 [password3 ...]]
* Plain-text passwords in quotes (or with spaces backslash escaped).
*
* @todo Port to a console command. https://www.drupal.org/node/2289409
*/
use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
if (PHP_SAPI !== 'cli') {
return;
}
$script = basename(array_shift($_SERVER['argv']));
if (in_array('--help', $_SERVER['argv']) || empty($_SERVER['argv'])) {
echo <<<EOF
Generate Drupal password hashes from the shell.
Usage: {$script} [OPTIONS] "<plaintext password>"
Example: {$script} "my-new-password"
All arguments are long options.
--help Print this page.
"<password1>" ["<password2>" ["<password3>" ...]]
One or more plaintext passwords enclosed by double quotes. The
output hash may be manually entered into the
{users_field_data}.pass field to change a password via SQL to a
known value.
EOF;
exit;
}
// Password list to be processed.
$passwords = $_SERVER['argv'];
$autoloader = require __DIR__ . '/../../autoload.php';
$request = Request::createFromGlobals();
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod', FALSE);
$kernel->boot();
$password_hasher = $kernel->getContainer()->get('password');
foreach ($passwords as $password) {
print("\npassword: $password \t\thash: " . $password_hasher->hash($password) . "\n");
}
print("\n");

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env php
<?php
/**
* @file
* Command line token calculator for rebuild.php.
*/
use Drupal\Component\Utility\Crypt;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\Request;
if (PHP_SAPI !== 'cli') {
return;
}
$autoloader = require __DIR__ . '/../../autoload.php';
$request = Request::createFromGlobals();
Settings::initialize(DRUPAL_ROOT, DrupalKernel::findSitePath($request), $autoloader);
$timestamp = time();
$token = Crypt::hmacBase64($timestamp, Settings::get('hash_salt'));
print "timestamp=$timestamp&token=$token\n";

1390
core/scripts/run-tests.sh Executable file

File diff suppressed because it is too large Load Diff

24
core/scripts/test-site.php Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env php
<?php
/**
* @file
* A command line application to install Drupal for tests.
*/
use Drupal\TestSite\TestSiteApplication;
if (PHP_SAPI !== 'cli') {
return;
}
// Use the PHPUnit bootstrap to prime an autoloader that works for test classes.
// Note we have to disable the SYMFONY_DEPRECATIONS_HELPER to ensure deprecation
// notices are not triggered.
putenv('SYMFONY_DEPRECATIONS_HELPER=disabled');
require_once __DIR__ . '/../tests/bootstrap.php';
// The application version is 0.1.0 to indicate that it is for internal use only
// and not currently API.
$app = new TestSiteApplication('test-site', '0.1.0');
$app->run();

104
core/scripts/update-countries.sh Executable file
View File

@@ -0,0 +1,104 @@
#!/bin/php
<?php
/**
* @file
* Updates CLDR codes in CountryManager.php to latest data.
*
* We rely on the CLDR data set, because it is easily accessible, scriptable,
* and in the right human-readable format.
*/
use Drupal\Core\Locale\CountryManager;
// cspell:ignore localenames
// Determine DRUPAL_ROOT.
$dir = dirname(__FILE__);
while (!defined('DRUPAL_ROOT')) {
if (is_dir($dir . '/core')) {
define('DRUPAL_ROOT', $dir);
}
$dir = dirname($dir);
}
// Determine source data file URI to process.
$uri = DRUPAL_ROOT . '/territories.json';
if (!file_exists($uri)) {
$usage = <<< USAGE
- Download territories.json from
https://github.com/unicode-org/cldr-json/blob/main/cldr-json/cldr-localenames-full/main/en/territories.json
and place it in the Drupal root directory.
- Run this script.
USAGE;
exit('CLDR data file not found. (' . $uri . ")\n\n" . $usage . "\n");
}
// Read in existing codes.
// @todo Allow to remove previously existing country codes.
// @see https://www.drupal.org/node/1436754
require_once DRUPAL_ROOT . '/core/lib/Drupal/Core/Locale/CountryManagerInterface.php';
require_once DRUPAL_ROOT . '/core/lib/Drupal/Core/Locale/CountryManager.php';
$existing_countries = CountryManager::getStandardList();
$countries = $existing_countries;
// Parse the source data into an array.
$data = json_decode(file_get_contents($uri));
foreach ($data->main->en->localeDisplayNames->territories as $code => $name) {
// Use any alternate codes the Drupal community wishes to.
$alt_codes = [
// 'CI-alt-variant', // Use CI-alt-variant instead of the CI entry.
];
if (in_array($code, $alt_codes)) {
// Just use the first 2 character part of the alt code.
$code = strtok($code, '-');
}
// Skip any codes we wish to exclude from our country list.
$exclude_codes = [
// The European Union is not a country.
'EU',
// The Eurozone is not a country.
'EZ',
// The United Nations is not a country.
'UN',
// "Pseudo-Accents" is not a country.
'XA',
// "Pseudo-Bidi" is not a country.
'XB',
// Don't allow "Unknown Region".
'ZZ',
];
if (in_array($code, $exclude_codes)) {
continue;
}
// Ignore every territory that doesn't have a 2 character code.
if (strlen($code) !== 2) {
continue;
}
$countries[(string) $code] = $name;
}
if (empty($countries)) {
echo 'ERROR: Did not find expected country names.' . PHP_EOL;
exit;
}
// Sort by country code (to minimize diffs).
ksort($countries);
// Produce PHP code.
$out = '';
foreach ($countries as $code => $name) {
// For .po translation file's sake, use double-quotes instead of escaped
// single-quotes.
$name = str_contains($name, '\'' ? '"' . $name . '"' : "'" . $name . "'");
$out .= ' ' . var_export($code, TRUE) . ' => t(' . $name . '),' . "\n";
}
// Replace the actual PHP code in standard.inc.
$file = DRUPAL_ROOT . '/core/lib/Drupal/Core/Locale/CountryManager.php';
$content = file_get_contents($file);
$content = preg_replace('/(\$countries = \[\n)(.+?)(^\s+\];)/ms', '$1' . $out . '$3', $content, -1, $count);
file_put_contents($file, $content);